brintos

brintos / linux-shallow public Read only

0
0
Text · 13.6 KiB · 5b5082e Raw
552 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014 Google, Inc.4 *5 * Copyright (C) 2022 Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt>6 *7 * Adapted from the downstream Pixel C driver written by Sean Paul8 */9 10#include <linux/backlight.h>11#include <linux/delay.h>12#include <linux/gpio/consumer.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/regulator/consumer.h>16 17#include <video/mipi_display.h>18 19#include <drm/drm_crtc.h>20#include <drm/drm_mipi_dsi.h>21#include <drm/drm_panel.h>22 23#define MCS_CMD_ACS_PROT			0xB024#define MCS_CMD_ACS_PROT_OFF			(0 << 0)25 26#define MCS_PWR_CTRL_FUNC			0xD027#define MCS_PWR_CTRL_PARAM1_DEFAULT		(2 << 0)28#define MCS_PWR_CTRL_PARAM1_VGH_210_DIV		(1 << 4)29#define MCS_PWR_CTRL_PARAM1_VGH_240_DIV		(2 << 4)30#define MCS_PWR_CTRL_PARAM1_VGH_280_DIV		(3 << 4)31#define MCS_PWR_CTRL_PARAM1_VGH_330_DIV		(4 << 4)32#define MCS_PWR_CTRL_PARAM1_VGH_410_DIV		(5 << 4)33#define MCS_PWR_CTRL_PARAM2_DEFAULT		(9 << 4)34#define MCS_PWR_CTRL_PARAM2_VGL_210_DIV		(1 << 0)35#define MCS_PWR_CTRL_PARAM2_VGL_240_DIV		(2 << 0)36#define MCS_PWR_CTRL_PARAM2_VGL_280_DIV		(3 << 0)37#define MCS_PWR_CTRL_PARAM2_VGL_330_DIV		(4 << 0)38#define MCS_PWR_CTRL_PARAM2_VGL_410_DIV		(5 << 0)39 40struct jdi_panel {41	struct drm_panel base;42	struct mipi_dsi_device *link1;43	struct mipi_dsi_device *link2;44 45	struct regulator *supply;46	struct regulator *ddi_supply;47	struct backlight_device *backlight;48 49	struct gpio_desc *enable_gpio;50	struct gpio_desc *reset_gpio;51 52	const struct drm_display_mode *mode;53};54 55static inline struct jdi_panel *to_panel_jdi(struct drm_panel *panel)56{57	return container_of(panel, struct jdi_panel, base);58}59 60static void jdi_wait_frames(struct jdi_panel *jdi, unsigned int frames)61{62	unsigned int refresh = drm_mode_vrefresh(jdi->mode);63 64	if (WARN_ON(frames > refresh))65		return;66 67	msleep(1000 / (refresh / frames));68}69 70static int jdi_panel_disable(struct drm_panel *panel)71{72	struct jdi_panel *jdi = to_panel_jdi(panel);73 74	backlight_disable(jdi->backlight);75 76	jdi_wait_frames(jdi, 2);77 78	return 0;79}80 81static int jdi_panel_unprepare(struct drm_panel *panel)82{83	struct jdi_panel *jdi = to_panel_jdi(panel);84	int ret;85 86	ret = mipi_dsi_dcs_set_display_off(jdi->link1);87	if (ret < 0)88		dev_err(panel->dev, "failed to set display off: %d\n", ret);89 90	ret = mipi_dsi_dcs_set_display_off(jdi->link2);91	if (ret < 0)92		dev_err(panel->dev, "failed to set display off: %d\n", ret);93 94	/* Specified by JDI @ 50ms, subject to change */95	msleep(50);96 97	ret = mipi_dsi_dcs_enter_sleep_mode(jdi->link1);98	if (ret < 0)99		dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);100	ret = mipi_dsi_dcs_enter_sleep_mode(jdi->link2);101	if (ret < 0)102		dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);103 104	/* Specified by JDI @ 150ms, subject to change */105	msleep(150);106 107	gpiod_set_value(jdi->reset_gpio, 1);108 109	/* T4 = 1ms */110	usleep_range(1000, 3000);111 112	gpiod_set_value(jdi->enable_gpio, 0);113 114	/* T5 = 2ms */115	usleep_range(2000, 4000);116 117	regulator_disable(jdi->ddi_supply);118 119	/* T6 = 2ms plus some time to discharge capacitors */120	usleep_range(7000, 9000);121 122	regulator_disable(jdi->supply);123	/* Specified by JDI @ 20ms, subject to change */124	msleep(20);125 126	return ret;127}128 129static int jdi_setup_symmetrical_split(struct mipi_dsi_device *left,130				       struct mipi_dsi_device *right,131				       const struct drm_display_mode *mode)132{133	int err;134 135	err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1);136	if (err < 0) {137		dev_err(&left->dev, "failed to set column address: %d\n", err);138		return err;139	}140 141	err = mipi_dsi_dcs_set_column_address(right, 0, mode->hdisplay / 2 - 1);142	if (err < 0) {143		dev_err(&right->dev, "failed to set column address: %d\n", err);144		return err;145	}146 147	err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1);148	if (err < 0) {149		dev_err(&left->dev, "failed to set page address: %d\n", err);150		return err;151	}152 153	err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1);154	if (err < 0) {155		dev_err(&right->dev, "failed to set page address: %d\n", err);156		return err;157	}158 159	return 0;160}161 162static int jdi_write_dcdc_registers(struct jdi_panel *jdi)163{164	/* Clear the manufacturer command access protection */165	mipi_dsi_generic_write_seq(jdi->link1, MCS_CMD_ACS_PROT,166				   MCS_CMD_ACS_PROT_OFF);167	mipi_dsi_generic_write_seq(jdi->link2, MCS_CMD_ACS_PROT,168				   MCS_CMD_ACS_PROT_OFF);169	/*170	 * Change the VGH/VGL divide rations to move the noise generated by the171	 * TCONN. This should hopefully avoid interaction with the backlight172	 * controller.173	 */174	mipi_dsi_generic_write_seq(jdi->link1, MCS_PWR_CTRL_FUNC,175				   MCS_PWR_CTRL_PARAM1_VGH_330_DIV |176				   MCS_PWR_CTRL_PARAM1_DEFAULT,177				   MCS_PWR_CTRL_PARAM2_VGL_410_DIV |178				   MCS_PWR_CTRL_PARAM2_DEFAULT);179 180	mipi_dsi_generic_write_seq(jdi->link2, MCS_PWR_CTRL_FUNC,181				   MCS_PWR_CTRL_PARAM1_VGH_330_DIV |182				   MCS_PWR_CTRL_PARAM1_DEFAULT,183				   MCS_PWR_CTRL_PARAM2_VGL_410_DIV |184				   MCS_PWR_CTRL_PARAM2_DEFAULT);185 186	return 0;187}188 189static int jdi_panel_prepare(struct drm_panel *panel)190{191	struct jdi_panel *jdi = to_panel_jdi(panel);192	int err;193 194	/* Disable backlight to avoid showing random pixels195	 * with a conservative delay for it to take effect.196	 */197	backlight_disable(jdi->backlight);198	jdi_wait_frames(jdi, 3);199 200	jdi->link1->mode_flags |= MIPI_DSI_MODE_LPM;201	jdi->link2->mode_flags |= MIPI_DSI_MODE_LPM;202 203	err = regulator_enable(jdi->supply);204	if (err < 0) {205		dev_err(panel->dev, "failed to enable supply: %d\n", err);206		return err;207	}208	/* T1 = 2ms */209	usleep_range(2000, 4000);210 211	err = regulator_enable(jdi->ddi_supply);212	if (err < 0) {213		dev_err(panel->dev, "failed to enable ddi_supply: %d\n", err);214		goto supply_off;215	}216	/* T2 = 1ms */217	usleep_range(1000, 3000);218 219	gpiod_set_value(jdi->enable_gpio, 1);220	/* T3 = 10ms */221	usleep_range(10000, 15000);222 223	gpiod_set_value(jdi->reset_gpio, 0);224	/* Specified by JDI @ 3ms, subject to change */225	usleep_range(3000, 5000);226 227	/*228	 * TODO: The device supports both left-right and even-odd split229	 * configurations, but this driver currently supports only the left-230	 * right split. To support a different mode a mechanism needs to be231	 * put in place to communicate the configuration back to the DSI host232	 * controller.233	 */234	err = jdi_setup_symmetrical_split(jdi->link1, jdi->link2,235					  jdi->mode);236	if (err < 0) {237		dev_err(panel->dev, "failed to set up symmetrical split: %d\n",238			err);239		goto poweroff;240	}241 242	err = mipi_dsi_dcs_set_tear_scanline(jdi->link1,243					     jdi->mode->vdisplay - 16);244	if (err < 0) {245		dev_err(panel->dev, "failed to set tear scanline: %d\n", err);246		goto poweroff;247	}248 249	err = mipi_dsi_dcs_set_tear_scanline(jdi->link2,250					     jdi->mode->vdisplay - 16);251	if (err < 0) {252		dev_err(panel->dev, "failed to set tear scanline: %d\n", err);253		goto poweroff;254	}255 256	err = mipi_dsi_dcs_set_tear_on(jdi->link1,257				       MIPI_DSI_DCS_TEAR_MODE_VBLANK);258	if (err < 0) {259		dev_err(panel->dev, "failed to set tear on: %d\n", err);260		goto poweroff;261	}262 263	err = mipi_dsi_dcs_set_tear_on(jdi->link2,264				       MIPI_DSI_DCS_TEAR_MODE_VBLANK);265	if (err < 0) {266		dev_err(panel->dev, "failed to set tear on: %d\n", err);267		goto poweroff;268	}269 270	err = mipi_dsi_dcs_set_pixel_format(jdi->link1, MIPI_DCS_PIXEL_FMT_24BIT);271	if (err < 0) {272		dev_err(panel->dev, "failed to set pixel format: %d\n", err);273		goto poweroff;274	}275 276	err = mipi_dsi_dcs_set_pixel_format(jdi->link2, MIPI_DCS_PIXEL_FMT_24BIT);277	if (err < 0) {278		dev_err(panel->dev, "failed to set pixel format: %d\n", err);279		goto poweroff;280	}281 282	err = mipi_dsi_dcs_exit_sleep_mode(jdi->link1);283	if (err < 0) {284		dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);285		goto poweroff;286	}287 288	err = mipi_dsi_dcs_exit_sleep_mode(jdi->link2);289	if (err < 0) {290		dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);291		goto poweroff;292	}293 294	err = jdi_write_dcdc_registers(jdi);295	if (err < 0) {296		dev_err(panel->dev, "failed to write dcdc registers: %d\n", err);297		goto poweroff;298	}299	/*300	 * We need to wait 150ms between mipi_dsi_dcs_exit_sleep_mode() and301	 * mipi_dsi_dcs_set_display_on().302	 */303	msleep(150);304 305	err = mipi_dsi_dcs_set_display_on(jdi->link1);306	if (err < 0) {307		dev_err(panel->dev, "failed to set display on: %d\n", err);308		goto poweroff;309	}310 311	err = mipi_dsi_dcs_set_display_on(jdi->link2);312	if (err < 0) {313		dev_err(panel->dev, "failed to set display on: %d\n", err);314		goto poweroff;315	}316 317	jdi->link1->mode_flags &= ~MIPI_DSI_MODE_LPM;318	jdi->link2->mode_flags &= ~MIPI_DSI_MODE_LPM;319 320	return 0;321 322poweroff:323	regulator_disable(jdi->ddi_supply);324 325	/* T6 = 2ms plus some time to discharge capacitors */326	usleep_range(7000, 9000);327supply_off:328	regulator_disable(jdi->supply);329	/* Specified by JDI @ 20ms, subject to change */330	msleep(20);331 332	return err;333}334 335static int jdi_panel_enable(struct drm_panel *panel)336{337	struct jdi_panel *jdi = to_panel_jdi(panel);338 339	/*340	 * Ensure we send image data before turning the backlight341	 * on, to avoid the display showing random pixels.342	 */343	jdi_wait_frames(jdi, 3);344 345	backlight_enable(jdi->backlight);346 347	return 0;348}349 350static const struct drm_display_mode default_mode = {351	.clock = (2560 + 80 + 80 + 80) * (1800 + 4 + 4 + 4) * 60 / 1000,352	.hdisplay = 2560,353	.hsync_start = 2560 + 80,354	.hsync_end = 2560 + 80 + 80,355	.htotal = 2560 + 80 + 80 + 80,356	.vdisplay = 1800,357	.vsync_start = 1800 + 4,358	.vsync_end = 1800 + 4 + 4,359	.vtotal = 1800 + 4 + 4 + 4,360	.flags = 0,361};362 363static int jdi_panel_get_modes(struct drm_panel *panel,364			       struct drm_connector *connector)365{366	struct drm_display_mode *mode;367	struct jdi_panel *jdi = to_panel_jdi(panel);368	struct device *dev = &jdi->link1->dev;369 370	mode = drm_mode_duplicate(connector->dev, &default_mode);371	if (!mode) {372		dev_err(dev, "failed to add mode %ux%ux@%u\n",373			default_mode.hdisplay, default_mode.vdisplay,374			drm_mode_vrefresh(&default_mode));375		return -ENOMEM;376	}377 378	drm_mode_set_name(mode);379 380	drm_mode_probed_add(connector, mode);381 382	connector->display_info.width_mm = 211;383	connector->display_info.height_mm = 148;384	connector->display_info.bpc = 8;385 386	return 1;387}388 389static const struct drm_panel_funcs jdi_panel_funcs = {390	.prepare = jdi_panel_prepare,391	.enable = jdi_panel_enable,392	.disable = jdi_panel_disable,393	.unprepare = jdi_panel_unprepare,394	.get_modes = jdi_panel_get_modes,395};396 397static const struct of_device_id jdi_of_match[] = {398	{ .compatible = "jdi,lpm102a188a", },399	{ }400};401MODULE_DEVICE_TABLE(of, jdi_of_match);402 403static int jdi_panel_add(struct jdi_panel *jdi)404{405	struct device *dev = &jdi->link1->dev;406 407	jdi->mode = &default_mode;408 409	jdi->supply = devm_regulator_get(dev, "power");410	if (IS_ERR(jdi->supply))411		return dev_err_probe(dev, PTR_ERR(jdi->supply),412				     "failed to get power regulator\n");413 414	jdi->ddi_supply = devm_regulator_get(dev, "ddi");415	if (IS_ERR(jdi->ddi_supply))416		return dev_err_probe(dev, PTR_ERR(jdi->ddi_supply),417				     "failed to get ddi regulator\n");418 419	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);420	if (IS_ERR(jdi->reset_gpio))421		return dev_err_probe(dev, PTR_ERR(jdi->reset_gpio),422				     "failed to get reset gpio\n");423	/* T4 = 1ms */424	usleep_range(1000, 3000);425 426	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);427	if (IS_ERR(jdi->enable_gpio))428		return dev_err_probe(dev, PTR_ERR(jdi->enable_gpio),429				     "failed to get enable gpio\n");430	/* T5 = 2ms */431	usleep_range(2000, 4000);432 433	jdi->backlight = devm_of_find_backlight(dev);434	if (IS_ERR(jdi->backlight))435		return dev_err_probe(dev, PTR_ERR(jdi->backlight),436				     "failed to create backlight\n");437 438	drm_panel_init(&jdi->base, &jdi->link1->dev, &jdi_panel_funcs,439		       DRM_MODE_CONNECTOR_DSI);440 441	drm_panel_add(&jdi->base);442 443	return 0;444}445 446static void jdi_panel_del(struct jdi_panel *jdi)447{448	if (jdi->base.dev)449		drm_panel_remove(&jdi->base);450 451	if (jdi->link2)452		put_device(&jdi->link2->dev);453}454 455static int jdi_panel_dsi_probe(struct mipi_dsi_device *dsi)456{457	struct mipi_dsi_device *secondary = NULL;458	struct jdi_panel *jdi;459	struct device_node *np;460	int err;461 462	dsi->lanes = 4;463	dsi->format = MIPI_DSI_FMT_RGB888;464	dsi->mode_flags = 0;465 466	/* Find DSI-LINK1 */467	np = of_parse_phandle(dsi->dev.of_node, "link2", 0);468	if (np) {469		secondary = of_find_mipi_dsi_device_by_node(np);470		of_node_put(np);471 472		if (!secondary)473			return -EPROBE_DEFER;474	}475 476	/* register a panel for only the DSI-LINK1 interface */477	if (secondary) {478		jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);479		if (!jdi) {480			put_device(&secondary->dev);481			return -ENOMEM;482		}483 484		mipi_dsi_set_drvdata(dsi, jdi);485 486		jdi->link1 = dsi;487		jdi->link2 = secondary;488 489		err = jdi_panel_add(jdi);490		if (err < 0) {491			put_device(&secondary->dev);492			return err;493		}494	}495 496	err = mipi_dsi_attach(dsi);497	if (err < 0) {498		if (secondary)499			jdi_panel_del(jdi);500 501		return err;502	}503 504	return 0;505}506 507static void jdi_panel_dsi_remove(struct mipi_dsi_device *dsi)508{509	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);510	int err;511 512	/* only detach from host for the DSI-LINK2 interface */513	if (!jdi)514		mipi_dsi_detach(dsi);515 516	err = jdi_panel_disable(&jdi->base);517	if (err < 0)518		dev_err(&dsi->dev, "failed to disable panel: %d\n", err);519 520	err = mipi_dsi_detach(dsi);521	if (err < 0)522		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);523 524	jdi_panel_del(jdi);525}526 527static void jdi_panel_dsi_shutdown(struct mipi_dsi_device *dsi)528{529	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);530 531	if (!jdi)532		return;533 534	jdi_panel_disable(&jdi->base);535}536 537static struct mipi_dsi_driver jdi_panel_dsi_driver = {538	.driver = {539		.name = "panel-jdi-lpm102a188a",540		.of_match_table = jdi_of_match,541	},542	.probe = jdi_panel_dsi_probe,543	.remove = jdi_panel_dsi_remove,544	.shutdown = jdi_panel_dsi_shutdown,545};546module_mipi_dsi_driver(jdi_panel_dsi_driver);547 548MODULE_AUTHOR("Sean Paul <seanpaul@chromium.org>");549MODULE_AUTHOR("Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt>");550MODULE_DESCRIPTION("DRM Driver for JDI LPM102A188A DSI panel, command mode");551MODULE_LICENSE("GPL");552