brintos

brintos / linux-shallow public Read only

0
0
Text · 15.0 KiB · d95c0d4 Raw
485 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd4 */5 6#include <linux/delay.h>7#include <linux/gpio/consumer.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/regulator/consumer.h>11 12#include <video/mipi_display.h>13 14#include <drm/drm_crtc.h>15#include <drm/drm_device.h>16#include <drm/drm_mipi_dsi.h>17#include <drm/drm_modes.h>18#include <drm/drm_panel.h>19 20struct innolux_panel;21 22struct panel_desc {23	const struct drm_display_mode *mode;24	unsigned int bpc;25	struct {26		unsigned int width;27		unsigned int height;28	} size;29 30	unsigned long flags;31	enum mipi_dsi_pixel_format format;32	int (*init)(struct innolux_panel *innolux);33	unsigned int lanes;34	const char * const *supply_names;35	unsigned int num_supplies;36	unsigned int sleep_mode_delay;37	unsigned int power_down_delay;38};39 40struct innolux_panel {41	struct drm_panel base;42	struct mipi_dsi_device *link;43	const struct panel_desc *desc;44 45	struct regulator_bulk_data *supplies;46	struct gpio_desc *enable_gpio;47};48 49static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)50{51	return container_of(panel, struct innolux_panel, base);52}53 54static int innolux_panel_unprepare(struct drm_panel *panel)55{56	struct innolux_panel *innolux = to_innolux_panel(panel);57	int err;58 59	err = mipi_dsi_dcs_set_display_off(innolux->link);60	if (err < 0)61		dev_err(panel->dev, "failed to set display off: %d\n", err);62 63	err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);64	if (err < 0) {65		dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);66		return err;67	}68 69	if (innolux->desc->sleep_mode_delay)70		msleep(innolux->desc->sleep_mode_delay);71 72	gpiod_set_value_cansleep(innolux->enable_gpio, 0);73 74	if (innolux->desc->power_down_delay)75		msleep(innolux->desc->power_down_delay);76 77	err = regulator_bulk_disable(innolux->desc->num_supplies,78				     innolux->supplies);79	if (err < 0)80		return err;81 82	return 0;83}84 85static int innolux_panel_prepare(struct drm_panel *panel)86{87	struct innolux_panel *innolux = to_innolux_panel(panel);88	int err;89 90	gpiod_set_value_cansleep(innolux->enable_gpio, 0);91 92	err = regulator_bulk_enable(innolux->desc->num_supplies,93				    innolux->supplies);94	if (err < 0)95		return err;96 97	/* p079zca: t2 (20ms), p097pfg: t4 (15ms) */98	usleep_range(20000, 21000);99 100	gpiod_set_value_cansleep(innolux->enable_gpio, 1);101 102	/* p079zca: t4, p097pfg: t5 */103	usleep_range(20000, 21000);104 105	if (innolux->desc->init) {106		err = innolux->desc->init(innolux);107		if (err < 0)108			goto poweroff;109	}110 111	err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);112	if (err < 0) {113		dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);114		goto poweroff;115	}116 117	/* T6: 120ms - 1000ms*/118	msleep(120);119 120	err = mipi_dsi_dcs_set_display_on(innolux->link);121	if (err < 0) {122		dev_err(panel->dev, "failed to set display on: %d\n", err);123		goto poweroff;124	}125 126	/* T7: 5ms */127	usleep_range(5000, 6000);128 129	return 0;130 131poweroff:132	gpiod_set_value_cansleep(innolux->enable_gpio, 0);133	regulator_bulk_disable(innolux->desc->num_supplies, innolux->supplies);134 135	return err;136}137 138static const char * const innolux_p079zca_supply_names[] = {139	"power",140};141 142static const struct drm_display_mode innolux_p079zca_mode = {143	.clock = 56900,144	.hdisplay = 768,145	.hsync_start = 768 + 40,146	.hsync_end = 768 + 40 + 40,147	.htotal = 768 + 40 + 40 + 40,148	.vdisplay = 1024,149	.vsync_start = 1024 + 20,150	.vsync_end = 1024 + 20 + 4,151	.vtotal = 1024 + 20 + 4 + 20,152};153 154static const struct panel_desc innolux_p079zca_panel_desc = {155	.mode = &innolux_p079zca_mode,156	.bpc = 8,157	.size = {158		.width = 120,159		.height = 160,160	},161	.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |162		 MIPI_DSI_MODE_LPM,163	.format = MIPI_DSI_FMT_RGB888,164	.lanes = 4,165	.supply_names = innolux_p079zca_supply_names,166	.num_supplies = ARRAY_SIZE(innolux_p079zca_supply_names),167	.power_down_delay = 80, /* T8: 80ms - 1000ms */168};169 170static const char * const innolux_p097pfg_supply_names[] = {171	"avdd",172	"avee",173};174 175static const struct drm_display_mode innolux_p097pfg_mode = {176	.clock = 229000,177	.hdisplay = 1536,178	.hsync_start = 1536 + 100,179	.hsync_end = 1536 + 100 + 24,180	.htotal = 1536 + 100 + 24 + 100,181	.vdisplay = 2048,182	.vsync_start = 2048 + 100,183	.vsync_end = 2048 + 100 + 2,184	.vtotal = 2048 + 100 + 2 + 18,185};186 187static void innolux_panel_write_multi(struct mipi_dsi_multi_context *ctx,188				      const void *payload, size_t size)189{190	mipi_dsi_generic_write_multi(ctx, payload, size);191 192	/*193	 * Included by random guessing, because without this194	 * (or at least, some delay), the panel sometimes195	 * didn't appear to pick up the command sequence.196	 */197	mipi_dsi_dcs_nop_multi(ctx);198}199 200#define innolux_panel_init_cmd_multi(ctx, seq...)                 \201	do {                                                      \202		static const u8 d[] = { seq };                    \203		innolux_panel_write_multi(ctx, d, ARRAY_SIZE(d)); \204	} while (0)205 206#define innolux_panel_switch_page(ctx, page) \207	innolux_panel_init_cmd_multi(ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, (page))208 209/*210 * Display manufacturer failed to provide init sequencing according to211 * https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/892065/212 * so the init sequence stems from a register dump of a working panel.213 */214static int innolux_p097pfg_init(struct innolux_panel *innolux)215{216	struct mipi_dsi_multi_context ctx = { .dsi = innolux->link };217 218	innolux_panel_switch_page(&ctx, 0x00);219	innolux_panel_init_cmd_multi(&ctx, 0xb1, 0xe8, 0x11);220	innolux_panel_init_cmd_multi(&ctx, 0xb2, 0x25, 0x02);221	innolux_panel_init_cmd_multi(&ctx, 0xb5, 0x08, 0x00);222	innolux_panel_init_cmd_multi(&ctx, 0xbc, 0x0f, 0x00);223	innolux_panel_init_cmd_multi(&ctx, 0xb8, 0x03, 0x06, 0x00, 0x00);224	innolux_panel_init_cmd_multi(&ctx, 0xbd, 0x01, 0x90, 0x14, 0x14);225	innolux_panel_init_cmd_multi(&ctx, 0x6f, 0x01);226	innolux_panel_init_cmd_multi(&ctx, 0xc0, 0x03);227	innolux_panel_init_cmd_multi(&ctx, 0x6f, 0x02);228	innolux_panel_init_cmd_multi(&ctx, 0xc1, 0x0d);229	innolux_panel_init_cmd_multi(&ctx, 0xd9, 0x01, 0x09, 0x70);230	innolux_panel_init_cmd_multi(&ctx, 0xc5, 0x12, 0x21, 0x00);231	innolux_panel_init_cmd_multi(&ctx, 0xbb, 0x93, 0x93);232 233	innolux_panel_switch_page(&ctx, 0x01);234	innolux_panel_init_cmd_multi(&ctx, 0xb3, 0x3c, 0x3c);235	innolux_panel_init_cmd_multi(&ctx, 0xb4, 0x0f, 0x0f);236	innolux_panel_init_cmd_multi(&ctx, 0xb9, 0x45, 0x45);237	innolux_panel_init_cmd_multi(&ctx, 0xba, 0x14, 0x14);238	innolux_panel_init_cmd_multi(&ctx, 0xca, 0x02);239	innolux_panel_init_cmd_multi(&ctx, 0xce, 0x04);240	innolux_panel_init_cmd_multi(&ctx, 0xc3, 0x9b, 0x9b);241	innolux_panel_init_cmd_multi(&ctx, 0xd8, 0xc0, 0x03);242	innolux_panel_init_cmd_multi(&ctx, 0xbc, 0x82, 0x01);243	innolux_panel_init_cmd_multi(&ctx, 0xbd, 0x9e, 0x01);244 245	innolux_panel_switch_page(&ctx, 0x02);246	innolux_panel_init_cmd_multi(&ctx, 0xb0, 0x82);247	innolux_panel_init_cmd_multi(&ctx, 0xd1, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x82, 0x00, 0xa5,248				     0x00, 0xc1, 0x00, 0xea, 0x01, 0x0d, 0x01, 0x40);249	innolux_panel_init_cmd_multi(&ctx, 0xd2, 0x01, 0x6a, 0x01, 0xa8, 0x01, 0xdc, 0x02, 0x29,250				     0x02, 0x67, 0x02, 0x68, 0x02, 0xa8, 0x02, 0xf0);251	innolux_panel_init_cmd_multi(&ctx, 0xd3, 0x03, 0x19, 0x03, 0x49, 0x03, 0x67, 0x03, 0x8c,252				     0x03, 0xa6, 0x03, 0xc7, 0x03, 0xde, 0x03, 0xec);253	innolux_panel_init_cmd_multi(&ctx, 0xd4, 0x03, 0xff, 0x03, 0xff);254	innolux_panel_init_cmd_multi(&ctx, 0xe0, 0x00, 0x00, 0x00, 0x86, 0x00, 0xc5, 0x00, 0xe5,255				     0x00, 0xff, 0x01, 0x26, 0x01, 0x45, 0x01, 0x75);256	innolux_panel_init_cmd_multi(&ctx, 0xe1, 0x01, 0x9c, 0x01, 0xd5, 0x02, 0x05, 0x02, 0x4d,257				     0x02, 0x86, 0x02, 0x87, 0x02, 0xc3, 0x03, 0x03);258	innolux_panel_init_cmd_multi(&ctx, 0xe2, 0x03, 0x2a, 0x03, 0x56, 0x03, 0x72, 0x03, 0x94,259				     0x03, 0xac, 0x03, 0xcb, 0x03, 0xe0, 0x03, 0xed);260	innolux_panel_init_cmd_multi(&ctx, 0xe3, 0x03, 0xff, 0x03, 0xff);261 262	innolux_panel_switch_page(&ctx, 0x03);263	innolux_panel_init_cmd_multi(&ctx, 0xb0, 0x00, 0x00, 0x00, 0x00);264	innolux_panel_init_cmd_multi(&ctx, 0xb1, 0x00, 0x00, 0x00, 0x00);265	innolux_panel_init_cmd_multi(&ctx, 0xb2, 0x00, 0x00, 0x06, 0x04, 0x01, 0x40, 0x85);266	innolux_panel_init_cmd_multi(&ctx, 0xb3, 0x10, 0x07, 0xfc, 0x04, 0x01, 0x40, 0x80);267	innolux_panel_init_cmd_multi(&ctx, 0xb6, 0xf0, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,268				     0x40, 0x80);269	innolux_panel_init_cmd_multi(&ctx, 0xba, 0xc5, 0x07, 0x00, 0x04, 0x11, 0x25, 0x8c);270	innolux_panel_init_cmd_multi(&ctx, 0xbb, 0xc5, 0x07, 0x00, 0x03, 0x11, 0x25, 0x8c);271	innolux_panel_init_cmd_multi(&ctx, 0xc0, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x80, 0x80);272	innolux_panel_init_cmd_multi(&ctx, 0xc1, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x80, 0x80);273	innolux_panel_init_cmd_multi(&ctx, 0xc4, 0x00, 0x00);274	innolux_panel_init_cmd_multi(&ctx, 0xef, 0x41);275 276	innolux_panel_switch_page(&ctx, 0x04);277	innolux_panel_init_cmd_multi(&ctx, 0xec, 0x4c);278 279	innolux_panel_switch_page(&ctx, 0x05);280	innolux_panel_init_cmd_multi(&ctx, 0xb0, 0x13, 0x03, 0x03, 0x01);281	innolux_panel_init_cmd_multi(&ctx, 0xb1, 0x30, 0x00);282	innolux_panel_init_cmd_multi(&ctx, 0xb2, 0x02, 0x02, 0x00);283	innolux_panel_init_cmd_multi(&ctx, 0xb3, 0x82, 0x23, 0x82, 0x9d);284	innolux_panel_init_cmd_multi(&ctx, 0xb4, 0xc5, 0x75, 0x24, 0x57);285	innolux_panel_init_cmd_multi(&ctx, 0xb5, 0x00, 0xd4, 0x72, 0x11, 0x11, 0xab, 0x0a);286	innolux_panel_init_cmd_multi(&ctx, 0xb6, 0x00, 0x00, 0xd5, 0x72, 0x24, 0x56);287	innolux_panel_init_cmd_multi(&ctx, 0xb7, 0x5c, 0xdc, 0x5c, 0x5c);288	innolux_panel_init_cmd_multi(&ctx, 0xb9, 0x0c, 0x00, 0x00, 0x01, 0x00);289	innolux_panel_init_cmd_multi(&ctx, 0xc0, 0x75, 0x11, 0x11, 0x54, 0x05);290	innolux_panel_init_cmd_multi(&ctx, 0xc6, 0x00, 0x00, 0x00, 0x00);291	innolux_panel_init_cmd_multi(&ctx, 0xd0, 0x00, 0x48, 0x08, 0x00, 0x00);292	innolux_panel_init_cmd_multi(&ctx, 0xd1, 0x00, 0x48, 0x09, 0x00, 0x00);293 294	innolux_panel_switch_page(&ctx, 0x06);295	innolux_panel_init_cmd_multi(&ctx, 0xb0, 0x02, 0x32, 0x32, 0x08, 0x2f);296	innolux_panel_init_cmd_multi(&ctx, 0xb1, 0x2e, 0x15, 0x14, 0x13, 0x12);297	innolux_panel_init_cmd_multi(&ctx, 0xb2, 0x11, 0x10, 0x00, 0x3d, 0x3d);298	innolux_panel_init_cmd_multi(&ctx, 0xb3, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d);299	innolux_panel_init_cmd_multi(&ctx, 0xb4, 0x3d, 0x32);300	innolux_panel_init_cmd_multi(&ctx, 0xb5, 0x03, 0x32, 0x32, 0x09, 0x2f);301	innolux_panel_init_cmd_multi(&ctx, 0xb6, 0x2e, 0x1b, 0x1a, 0x19, 0x18);302	innolux_panel_init_cmd_multi(&ctx, 0xb7, 0x17, 0x16, 0x01, 0x3d, 0x3d);303	innolux_panel_init_cmd_multi(&ctx, 0xb8, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d);304	innolux_panel_init_cmd_multi(&ctx, 0xb9, 0x3d, 0x32);305	innolux_panel_init_cmd_multi(&ctx, 0xc0, 0x01, 0x32, 0x32, 0x09, 0x2f);306	innolux_panel_init_cmd_multi(&ctx, 0xc1, 0x2e, 0x1a, 0x1b, 0x16, 0x17);307	innolux_panel_init_cmd_multi(&ctx, 0xc2, 0x18, 0x19, 0x03, 0x3d, 0x3d);308	innolux_panel_init_cmd_multi(&ctx, 0xc3, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d);309	innolux_panel_init_cmd_multi(&ctx, 0xc4, 0x3d, 0x32);310	innolux_panel_init_cmd_multi(&ctx, 0xc5, 0x00, 0x32, 0x32, 0x08, 0x2f);311	innolux_panel_init_cmd_multi(&ctx, 0xc6, 0x2e, 0x14, 0x15, 0x10, 0x11);312	innolux_panel_init_cmd_multi(&ctx, 0xc7, 0x12, 0x13, 0x02, 0x3d, 0x3d);313	innolux_panel_init_cmd_multi(&ctx, 0xc8, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d);314	innolux_panel_init_cmd_multi(&ctx, 0xc9, 0x3d, 0x32);315 316	return ctx.accum_err;317}318 319static const struct panel_desc innolux_p097pfg_panel_desc = {320	.mode = &innolux_p097pfg_mode,321	.bpc = 8,322	.size = {323		.width = 147,324		.height = 196,325	},326	.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |327		 MIPI_DSI_MODE_LPM,328	.format = MIPI_DSI_FMT_RGB888,329	.init = innolux_p097pfg_init,330	.lanes = 4,331	.supply_names = innolux_p097pfg_supply_names,332	.num_supplies = ARRAY_SIZE(innolux_p097pfg_supply_names),333	.sleep_mode_delay = 100, /* T15 */334};335 336static int innolux_panel_get_modes(struct drm_panel *panel,337				   struct drm_connector *connector)338{339	struct innolux_panel *innolux = to_innolux_panel(panel);340	const struct drm_display_mode *m = innolux->desc->mode;341	struct drm_display_mode *mode;342 343	mode = drm_mode_duplicate(connector->dev, m);344	if (!mode) {345		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",346			m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));347		return -ENOMEM;348	}349 350	drm_mode_set_name(mode);351 352	drm_mode_probed_add(connector, mode);353 354	connector->display_info.width_mm = innolux->desc->size.width;355	connector->display_info.height_mm = innolux->desc->size.height;356	connector->display_info.bpc = innolux->desc->bpc;357 358	return 1;359}360 361static const struct drm_panel_funcs innolux_panel_funcs = {362	.unprepare = innolux_panel_unprepare,363	.prepare = innolux_panel_prepare,364	.get_modes = innolux_panel_get_modes,365};366 367static const struct of_device_id innolux_of_match[] = {368	{ .compatible = "innolux,p079zca",369	  .data = &innolux_p079zca_panel_desc370	},371	{ .compatible = "innolux,p097pfg",372	  .data = &innolux_p097pfg_panel_desc373	},374	{ }375};376MODULE_DEVICE_TABLE(of, innolux_of_match);377 378static int innolux_panel_add(struct mipi_dsi_device *dsi,379			     const struct panel_desc *desc)380{381	struct innolux_panel *innolux;382	struct device *dev = &dsi->dev;383	int err, i;384 385	innolux = devm_kzalloc(dev, sizeof(*innolux), GFP_KERNEL);386	if (!innolux)387		return -ENOMEM;388 389	innolux->desc = desc;390 391	innolux->supplies = devm_kcalloc(dev, desc->num_supplies,392					 sizeof(*innolux->supplies),393					 GFP_KERNEL);394	if (!innolux->supplies)395		return -ENOMEM;396 397	for (i = 0; i < desc->num_supplies; i++)398		innolux->supplies[i].supply = desc->supply_names[i];399 400	err = devm_regulator_bulk_get(dev, desc->num_supplies,401				      innolux->supplies);402	if (err < 0)403		return err;404 405	innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",406						       GPIOD_OUT_HIGH);407	if (IS_ERR(innolux->enable_gpio)) {408		err = PTR_ERR(innolux->enable_gpio);409		dev_dbg(dev, "failed to get enable gpio: %d\n", err);410		innolux->enable_gpio = NULL;411	}412 413	drm_panel_init(&innolux->base, dev, &innolux_panel_funcs,414		       DRM_MODE_CONNECTOR_DSI);415 416	err = drm_panel_of_backlight(&innolux->base);417	if (err)418		return err;419 420	drm_panel_add(&innolux->base);421 422	mipi_dsi_set_drvdata(dsi, innolux);423	innolux->link = dsi;424 425	return 0;426}427 428static void innolux_panel_del(struct innolux_panel *innolux)429{430	drm_panel_remove(&innolux->base);431}432 433static int innolux_panel_probe(struct mipi_dsi_device *dsi)434{435	const struct panel_desc *desc;436	struct innolux_panel *innolux;437	int err;438 439	desc = of_device_get_match_data(&dsi->dev);440	dsi->mode_flags = desc->flags;441	dsi->format = desc->format;442	dsi->lanes = desc->lanes;443 444	err = innolux_panel_add(dsi, desc);445	if (err < 0)446		return err;447 448	err = mipi_dsi_attach(dsi);449	if (err < 0) {450		innolux = mipi_dsi_get_drvdata(dsi);451		innolux_panel_del(innolux);452		return err;453	}454 455	return 0;456}457 458static void innolux_panel_remove(struct mipi_dsi_device *dsi)459{460	struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);461	int err;462 463 464	err = mipi_dsi_detach(dsi);465	if (err < 0)466		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);467 468	innolux_panel_del(innolux);469}470 471static struct mipi_dsi_driver innolux_panel_driver = {472	.driver = {473		.name = "panel-innolux-p079zca",474		.of_match_table = innolux_of_match,475	},476	.probe = innolux_panel_probe,477	.remove = innolux_panel_remove,478};479module_mipi_dsi_driver(innolux_panel_driver);480 481MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");482MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");483MODULE_DESCRIPTION("Innolux P079ZCA panel driver");484MODULE_LICENSE("GPL v2");485