brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 729cbb0 Raw
296 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2015 Red Hat4 * Copyright (C) 2015 Sony Mobile Communications Inc.5 * Author: Werner Johansson <werner.johansson@sonymobile.com>6 *7 * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>8 */9 10#include <linux/delay.h>11#include <linux/gpio/consumer.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/regulator/consumer.h>15 16#include <video/mipi_display.h>17 18#include <drm/drm_crtc.h>19#include <drm/drm_device.h>20#include <drm/drm_mipi_dsi.h>21#include <drm/drm_panel.h>22 23struct sharp_nt_panel {24	struct drm_panel base;25	struct mipi_dsi_device *dsi;26 27	struct regulator *supply;28	struct gpio_desc *reset_gpio;29};30 31static inline struct sharp_nt_panel *to_sharp_nt_panel(struct drm_panel *panel)32{33	return container_of(panel, struct sharp_nt_panel, base);34}35 36static int sharp_nt_panel_init(struct sharp_nt_panel *sharp_nt)37{38	struct mipi_dsi_device *dsi = sharp_nt->dsi;39	int ret;40 41	dsi->mode_flags |= MIPI_DSI_MODE_LPM;42 43	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);44	if (ret < 0)45		return ret;46 47	msleep(120);48 49	/* Novatek two-lane operation */50	ret = mipi_dsi_dcs_write(dsi, 0xae, (u8[]){ 0x03 }, 1);51	if (ret < 0)52		return ret;53 54	/* Set both MCU and RGB I/F to 24bpp */55	ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |56					(MIPI_DCS_PIXEL_FMT_24BIT << 4));57	if (ret < 0)58		return ret;59 60	return 0;61}62 63static int sharp_nt_panel_on(struct sharp_nt_panel *sharp_nt)64{65	struct mipi_dsi_device *dsi = sharp_nt->dsi;66	int ret;67 68	dsi->mode_flags |= MIPI_DSI_MODE_LPM;69 70	ret = mipi_dsi_dcs_set_display_on(dsi);71	if (ret < 0)72		return ret;73 74	return 0;75}76 77static int sharp_nt_panel_off(struct sharp_nt_panel *sharp_nt)78{79	struct mipi_dsi_device *dsi = sharp_nt->dsi;80	int ret;81 82	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;83 84	ret = mipi_dsi_dcs_set_display_off(dsi);85	if (ret < 0)86		return ret;87 88	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);89	if (ret < 0)90		return ret;91 92	return 0;93}94 95static int sharp_nt_panel_unprepare(struct drm_panel *panel)96{97	struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);98	int ret;99 100	ret = sharp_nt_panel_off(sharp_nt);101	if (ret < 0) {102		dev_err(panel->dev, "failed to set panel off: %d\n", ret);103		return ret;104	}105 106	regulator_disable(sharp_nt->supply);107	if (sharp_nt->reset_gpio)108		gpiod_set_value(sharp_nt->reset_gpio, 0);109 110	return 0;111}112 113static int sharp_nt_panel_prepare(struct drm_panel *panel)114{115	struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);116	int ret;117 118	ret = regulator_enable(sharp_nt->supply);119	if (ret < 0)120		return ret;121 122	msleep(20);123 124	if (sharp_nt->reset_gpio) {125		gpiod_set_value(sharp_nt->reset_gpio, 1);126		msleep(1);127		gpiod_set_value(sharp_nt->reset_gpio, 0);128		msleep(1);129		gpiod_set_value(sharp_nt->reset_gpio, 1);130		msleep(10);131	}132 133	ret = sharp_nt_panel_init(sharp_nt);134	if (ret < 0) {135		dev_err(panel->dev, "failed to init panel: %d\n", ret);136		goto poweroff;137	}138 139	ret = sharp_nt_panel_on(sharp_nt);140	if (ret < 0) {141		dev_err(panel->dev, "failed to set panel on: %d\n", ret);142		goto poweroff;143	}144 145	return 0;146 147poweroff:148	regulator_disable(sharp_nt->supply);149	if (sharp_nt->reset_gpio)150		gpiod_set_value(sharp_nt->reset_gpio, 0);151	return ret;152}153 154static const struct drm_display_mode default_mode = {155	.clock = (540 + 48 + 32 + 80) * (960 + 3 + 10 + 15) * 60 / 1000,156	.hdisplay = 540,157	.hsync_start = 540 + 48,158	.hsync_end = 540 + 48 + 32,159	.htotal = 540 + 48 + 32 + 80,160	.vdisplay = 960,161	.vsync_start = 960 + 3,162	.vsync_end = 960 + 3 + 10,163	.vtotal = 960 + 3 + 10 + 15,164};165 166static int sharp_nt_panel_get_modes(struct drm_panel *panel,167				    struct drm_connector *connector)168{169	struct drm_display_mode *mode;170 171	mode = drm_mode_duplicate(connector->dev, &default_mode);172	if (!mode) {173		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",174			default_mode.hdisplay, default_mode.vdisplay,175			drm_mode_vrefresh(&default_mode));176		return -ENOMEM;177	}178 179	drm_mode_set_name(mode);180 181	drm_mode_probed_add(connector, mode);182 183	connector->display_info.width_mm = 54;184	connector->display_info.height_mm = 95;185 186	return 1;187}188 189static const struct drm_panel_funcs sharp_nt_panel_funcs = {190	.unprepare = sharp_nt_panel_unprepare,191	.prepare = sharp_nt_panel_prepare,192	.get_modes = sharp_nt_panel_get_modes,193};194 195static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt)196{197	struct device *dev = &sharp_nt->dsi->dev;198	int ret;199 200	sharp_nt->supply = devm_regulator_get(dev, "avdd");201	if (IS_ERR(sharp_nt->supply))202		return PTR_ERR(sharp_nt->supply);203 204	sharp_nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);205	if (IS_ERR(sharp_nt->reset_gpio)) {206		dev_err(dev, "cannot get reset-gpios %ld\n",207			PTR_ERR(sharp_nt->reset_gpio));208		sharp_nt->reset_gpio = NULL;209	} else {210		gpiod_set_value(sharp_nt->reset_gpio, 0);211	}212 213	drm_panel_init(&sharp_nt->base, &sharp_nt->dsi->dev,214		       &sharp_nt_panel_funcs, DRM_MODE_CONNECTOR_DSI);215 216	ret = drm_panel_of_backlight(&sharp_nt->base);217	if (ret)218		return ret;219 220	drm_panel_add(&sharp_nt->base);221 222	return 0;223}224 225static void sharp_nt_panel_del(struct sharp_nt_panel *sharp_nt)226{227	if (sharp_nt->base.dev)228		drm_panel_remove(&sharp_nt->base);229}230 231static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi)232{233	struct sharp_nt_panel *sharp_nt;234	int ret;235 236	dsi->lanes = 2;237	dsi->format = MIPI_DSI_FMT_RGB888;238	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |239			MIPI_DSI_MODE_VIDEO_SYNC_PULSE |240			MIPI_DSI_MODE_VIDEO_HSE |241			MIPI_DSI_CLOCK_NON_CONTINUOUS |242			MIPI_DSI_MODE_NO_EOT_PACKET;243 244	sharp_nt = devm_kzalloc(&dsi->dev, sizeof(*sharp_nt), GFP_KERNEL);245	if (!sharp_nt)246		return -ENOMEM;247 248	mipi_dsi_set_drvdata(dsi, sharp_nt);249 250	sharp_nt->dsi = dsi;251 252	ret = sharp_nt_panel_add(sharp_nt);253	if (ret < 0)254		return ret;255 256	ret = mipi_dsi_attach(dsi);257	if (ret < 0) {258		sharp_nt_panel_del(sharp_nt);259		return ret;260	}261 262	return 0;263}264 265static void sharp_nt_panel_remove(struct mipi_dsi_device *dsi)266{267	struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);268	int ret;269 270	ret = mipi_dsi_detach(dsi);271	if (ret < 0)272		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);273 274	sharp_nt_panel_del(sharp_nt);275}276 277static const struct of_device_id sharp_nt_of_match[] = {278	{ .compatible = "sharp,ls043t1le01-qhd", },279	{ }280};281MODULE_DEVICE_TABLE(of, sharp_nt_of_match);282 283static struct mipi_dsi_driver sharp_nt_panel_driver = {284	.driver = {285		.name = "panel-sharp-ls043t1le01-qhd",286		.of_match_table = sharp_nt_of_match,287	},288	.probe = sharp_nt_panel_probe,289	.remove = sharp_nt_panel_remove,290};291module_mipi_dsi_driver(sharp_nt_panel_driver);292 293MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");294MODULE_DESCRIPTION("Sharp LS043T1LE01 NT35565-based qHD (540x960) video mode panel driver");295MODULE_LICENSE("GPL v2");296