brintos

brintos / linux-shallow public Read only

0
0
Text · 13.6 KiB · 6b3f4d6 Raw
653 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Generic DSI Command Mode panel driver4 *5 * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>7 */8 9#include <linux/backlight.h>10#include <linux/delay.h>11#include <linux/gpio/consumer.h>12#include <linux/jiffies.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/regulator/consumer.h>16 17#include <drm/drm_connector.h>18#include <drm/drm_mipi_dsi.h>19#include <drm/drm_modes.h>20#include <drm/drm_panel.h>21 22#include <video/mipi_display.h>23 24#define DCS_GET_ID1		0xda25#define DCS_GET_ID2		0xdb26#define DCS_GET_ID3		0xdc27 28#define DCS_REGULATOR_SUPPLY_NUM 229 30static const struct of_device_id dsicm_of_match[];31 32struct dsic_panel_data {33	u32 xres;34	u32 yres;35	u32 refresh;36	u32 width_mm;37	u32 height_mm;38	u32 max_hs_rate;39	u32 max_lp_rate;40	bool te_support;41};42 43struct panel_drv_data {44	struct mipi_dsi_device *dsi;45	struct drm_panel panel;46	struct drm_display_mode mode;47 48	struct mutex lock;49 50	struct backlight_device *bldev;51	struct backlight_device *extbldev;52 53	unsigned long	hw_guard_end;	/* next value of jiffies when we can54					 * issue the next sleep in/out command55					 */56	unsigned long	hw_guard_wait;	/* max guard time in jiffies */57 58	const struct dsic_panel_data *panel_data;59 60	struct gpio_desc *reset_gpio;61 62	struct regulator_bulk_data supplies[DCS_REGULATOR_SUPPLY_NUM];63 64	bool use_dsi_backlight;65 66	/* runtime variables */67	bool enabled;68 69	bool intro_printed;70};71 72static inline struct panel_drv_data *panel_to_ddata(struct drm_panel *panel)73{74	return container_of(panel, struct panel_drv_data, panel);75}76 77static void dsicm_bl_power(struct panel_drv_data *ddata, bool enable)78{79	struct backlight_device *backlight;80 81	if (ddata->bldev)82		backlight = ddata->bldev;83	else if (ddata->extbldev)84		backlight = ddata->extbldev;85	else86		return;87 88	if (enable)89		backlight_enable(backlight);90	else91		backlight_disable(backlight);92}93 94static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)95{96	ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);97	ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;98}99 100static void hw_guard_wait(struct panel_drv_data *ddata)101{102	unsigned long wait = ddata->hw_guard_end - jiffies;103 104	if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {105		set_current_state(TASK_UNINTERRUPTIBLE);106		schedule_timeout(wait);107	}108}109 110static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)111{112	return mipi_dsi_dcs_read(ddata->dsi, dcs_cmd, data, 1);113}114 115static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)116{117	return mipi_dsi_dcs_write(ddata->dsi, dcs_cmd, &param, 1);118}119 120static int dsicm_sleep_in(struct panel_drv_data *ddata)121 122{123	int r;124 125	hw_guard_wait(ddata);126 127	r = mipi_dsi_dcs_enter_sleep_mode(ddata->dsi);128	if (r)129		return r;130 131	hw_guard_start(ddata, 120);132 133	usleep_range(5000, 10000);134 135	return 0;136}137 138static int dsicm_sleep_out(struct panel_drv_data *ddata)139{140	int r;141 142	hw_guard_wait(ddata);143 144	r = mipi_dsi_dcs_exit_sleep_mode(ddata->dsi);145	if (r)146		return r;147 148	hw_guard_start(ddata, 120);149 150	usleep_range(5000, 10000);151 152	return 0;153}154 155static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)156{157	int r;158 159	r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1);160	if (r)161		return r;162	r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2);163	if (r)164		return r;165	r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3);166	if (r)167		return r;168 169	return 0;170}171 172static int dsicm_set_update_window(struct panel_drv_data *ddata)173{174	struct mipi_dsi_device *dsi = ddata->dsi;175	int r;176 177	r = mipi_dsi_dcs_set_column_address(dsi, 0, ddata->mode.hdisplay - 1);178	if (r < 0)179		return r;180 181	r = mipi_dsi_dcs_set_page_address(dsi, 0, ddata->mode.vdisplay - 1);182	if (r < 0)183		return r;184 185	return 0;186}187 188static int dsicm_bl_update_status(struct backlight_device *dev)189{190	struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);191	int r = 0;192	int level = backlight_get_brightness(dev);193 194	dev_dbg(&ddata->dsi->dev, "update brightness to %d\n", level);195 196	mutex_lock(&ddata->lock);197 198	if (ddata->enabled)199		r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,200				      level);201 202	mutex_unlock(&ddata->lock);203 204	return r;205}206 207static int dsicm_bl_get_intensity(struct backlight_device *dev)208{209	return backlight_get_brightness(dev);210}211 212static const struct backlight_ops dsicm_bl_ops = {213	.get_brightness = dsicm_bl_get_intensity,214	.update_status  = dsicm_bl_update_status,215};216 217static ssize_t num_dsi_errors_show(struct device *dev,218		struct device_attribute *attr, char *buf)219{220	struct panel_drv_data *ddata = dev_get_drvdata(dev);221	u8 errors = 0;222	int r = -ENODEV;223 224	mutex_lock(&ddata->lock);225 226	if (ddata->enabled)227		r = dsicm_dcs_read_1(ddata, MIPI_DCS_GET_ERROR_COUNT_ON_DSI, &errors);228 229	mutex_unlock(&ddata->lock);230 231	if (r)232		return r;233 234	return sysfs_emit(buf, "%d\n", errors);235}236 237static ssize_t hw_revision_show(struct device *dev,238		struct device_attribute *attr, char *buf)239{240	struct panel_drv_data *ddata = dev_get_drvdata(dev);241	u8 id1, id2, id3;242	int r = -ENODEV;243 244	mutex_lock(&ddata->lock);245 246	if (ddata->enabled)247		r = dsicm_get_id(ddata, &id1, &id2, &id3);248 249	mutex_unlock(&ddata->lock);250 251	if (r)252		return r;253 254	return sysfs_emit(buf, "%02x.%02x.%02x\n", id1, id2, id3);255}256 257static DEVICE_ATTR_RO(num_dsi_errors);258static DEVICE_ATTR_RO(hw_revision);259 260static struct attribute *dsicm_attrs[] = {261	&dev_attr_num_dsi_errors.attr,262	&dev_attr_hw_revision.attr,263	NULL,264};265 266static const struct attribute_group dsicm_attr_group = {267	.attrs = dsicm_attrs,268};269 270static void dsicm_hw_reset(struct panel_drv_data *ddata)271{272	gpiod_set_value(ddata->reset_gpio, 1);273	udelay(10);274	/* reset the panel */275	gpiod_set_value(ddata->reset_gpio, 0);276	/* assert reset */277	udelay(10);278	gpiod_set_value(ddata->reset_gpio, 1);279	/* wait after releasing reset */280	usleep_range(5000, 10000);281}282 283static int dsicm_power_on(struct panel_drv_data *ddata)284{285	u8 id1, id2, id3;286	int r;287 288	dsicm_hw_reset(ddata);289 290	ddata->dsi->mode_flags |= MIPI_DSI_MODE_LPM;291 292	r = dsicm_sleep_out(ddata);293	if (r)294		goto err;295 296	r = dsicm_get_id(ddata, &id1, &id2, &id3);297	if (r)298		goto err;299 300	r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff);301	if (r)302		goto err;303 304	r = dsicm_dcs_write_1(ddata, MIPI_DCS_WRITE_CONTROL_DISPLAY,305			(1<<2) | (1<<5));	/* BL | BCTRL */306	if (r)307		goto err;308 309	r = mipi_dsi_dcs_set_pixel_format(ddata->dsi, MIPI_DCS_PIXEL_FMT_24BIT);310	if (r)311		goto err;312 313	r = dsicm_set_update_window(ddata);314	if (r)315		goto err;316 317	r = mipi_dsi_dcs_set_display_on(ddata->dsi);318	if (r)319		goto err;320 321	if (ddata->panel_data->te_support) {322		r = mipi_dsi_dcs_set_tear_on(ddata->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);323		if (r)324			goto err;325	}326 327	/* possible panel bug */328	msleep(100);329 330	ddata->enabled = true;331 332	if (!ddata->intro_printed) {333		dev_info(&ddata->dsi->dev, "panel revision %02x.%02x.%02x\n",334			id1, id2, id3);335		ddata->intro_printed = true;336	}337 338	ddata->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;339 340	return 0;341err:342	dev_err(&ddata->dsi->dev, "error while enabling panel, issuing HW reset\n");343 344	dsicm_hw_reset(ddata);345 346	return r;347}348 349static int dsicm_power_off(struct panel_drv_data *ddata)350{351	int r;352 353	ddata->enabled = false;354 355	r = mipi_dsi_dcs_set_display_off(ddata->dsi);356	if (!r)357		r = dsicm_sleep_in(ddata);358 359	if (r) {360		dev_err(&ddata->dsi->dev,361				"error disabling panel, issuing HW reset\n");362		dsicm_hw_reset(ddata);363	}364 365	return r;366}367 368static int dsicm_prepare(struct drm_panel *panel)369{370	struct panel_drv_data *ddata = panel_to_ddata(panel);371	int r;372 373	r = regulator_bulk_enable(ARRAY_SIZE(ddata->supplies), ddata->supplies);374	if (r)375		dev_err(&ddata->dsi->dev, "failed to enable supplies: %d\n", r);376 377	return r;378}379 380static int dsicm_enable(struct drm_panel *panel)381{382	struct panel_drv_data *ddata = panel_to_ddata(panel);383	int r;384 385	mutex_lock(&ddata->lock);386 387	r = dsicm_power_on(ddata);388	if (r)389		goto err;390 391	mutex_unlock(&ddata->lock);392 393	dsicm_bl_power(ddata, true);394 395	return 0;396err:397	dev_err(&ddata->dsi->dev, "enable failed (%d)\n", r);398	mutex_unlock(&ddata->lock);399	return r;400}401 402static int dsicm_unprepare(struct drm_panel *panel)403{404	struct panel_drv_data *ddata = panel_to_ddata(panel);405	int r;406 407	r = regulator_bulk_disable(ARRAY_SIZE(ddata->supplies), ddata->supplies);408	if (r)409		dev_err(&ddata->dsi->dev, "failed to disable supplies: %d\n", r);410 411	return r;412}413 414static int dsicm_disable(struct drm_panel *panel)415{416	struct panel_drv_data *ddata = panel_to_ddata(panel);417	int r;418 419	dsicm_bl_power(ddata, false);420 421	mutex_lock(&ddata->lock);422 423	r = dsicm_power_off(ddata);424 425	mutex_unlock(&ddata->lock);426 427	return r;428}429 430static int dsicm_get_modes(struct drm_panel *panel,431			   struct drm_connector *connector)432{433	struct panel_drv_data *ddata = panel_to_ddata(panel);434	struct drm_display_mode *mode;435 436	mode = drm_mode_duplicate(connector->dev, &ddata->mode);437	if (!mode) {438		dev_err(&ddata->dsi->dev, "failed to add mode %ux%ux@%u kHz\n",439			ddata->mode.hdisplay, ddata->mode.vdisplay,440			ddata->mode.clock);441		return -ENOMEM;442	}443 444	connector->display_info.width_mm = ddata->panel_data->width_mm;445	connector->display_info.height_mm = ddata->panel_data->height_mm;446 447	drm_mode_probed_add(connector, mode);448 449	return 1;450}451 452static const struct drm_panel_funcs dsicm_panel_funcs = {453	.unprepare = dsicm_unprepare,454	.disable = dsicm_disable,455	.prepare = dsicm_prepare,456	.enable = dsicm_enable,457	.get_modes = dsicm_get_modes,458};459 460static int dsicm_probe_of(struct mipi_dsi_device *dsi)461{462	struct backlight_device *backlight;463	struct panel_drv_data *ddata = mipi_dsi_get_drvdata(dsi);464	int err;465	struct drm_display_mode *mode = &ddata->mode;466 467	ddata->reset_gpio = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);468	if (IS_ERR(ddata->reset_gpio)) {469		err = PTR_ERR(ddata->reset_gpio);470		dev_err(&dsi->dev, "reset gpio request failed: %d", err);471		return err;472	}473 474	mode->hdisplay = mode->hsync_start = mode->hsync_end = mode->htotal =475		ddata->panel_data->xres;476	mode->vdisplay = mode->vsync_start = mode->vsync_end = mode->vtotal =477		ddata->panel_data->yres;478	mode->clock = ddata->panel_data->xres * ddata->panel_data->yres *479		ddata->panel_data->refresh / 1000;480	mode->width_mm = ddata->panel_data->width_mm;481	mode->height_mm = ddata->panel_data->height_mm;482	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;483	drm_mode_set_name(mode);484 485	ddata->supplies[0].supply = "vpnl";486	ddata->supplies[1].supply = "vddi";487	err = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ddata->supplies),488				      ddata->supplies);489	if (err)490		return err;491 492	backlight = devm_of_find_backlight(&dsi->dev);493	if (IS_ERR(backlight))494		return PTR_ERR(backlight);495 496	/* If no backlight device is found assume native backlight support */497	if (backlight)498		ddata->extbldev = backlight;499	else500		ddata->use_dsi_backlight = true;501 502	return 0;503}504 505static int dsicm_probe(struct mipi_dsi_device *dsi)506{507	struct panel_drv_data *ddata;508	struct backlight_device *bldev = NULL;509	struct device *dev = &dsi->dev;510	int r;511 512	dev_dbg(dev, "probe\n");513 514	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);515	if (!ddata)516		return -ENOMEM;517 518	mipi_dsi_set_drvdata(dsi, ddata);519	ddata->dsi = dsi;520 521	ddata->panel_data = of_device_get_match_data(dev);522	if (!ddata->panel_data)523		return -ENODEV;524 525	r = dsicm_probe_of(dsi);526	if (r)527		return r;528 529	mutex_init(&ddata->lock);530 531	dsicm_hw_reset(ddata);532 533	drm_panel_init(&ddata->panel, dev, &dsicm_panel_funcs,534		       DRM_MODE_CONNECTOR_DSI);535 536	if (ddata->use_dsi_backlight) {537		struct backlight_properties props = { 0 };538		props.max_brightness = 255;539		props.type = BACKLIGHT_RAW;540 541		bldev = devm_backlight_device_register(dev, dev_name(dev),542			dev, ddata, &dsicm_bl_ops, &props);543		if (IS_ERR(bldev)) {544			r = PTR_ERR(bldev);545			goto err_bl;546		}547 548		ddata->bldev = bldev;549	}550 551	r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);552	if (r) {553		dev_err(dev, "failed to create sysfs files\n");554		goto err_bl;555	}556 557	dsi->lanes = 2;558	dsi->format = MIPI_DSI_FMT_RGB888;559	dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS |560			  MIPI_DSI_MODE_NO_EOT_PACKET;561	dsi->hs_rate = ddata->panel_data->max_hs_rate;562	dsi->lp_rate = ddata->panel_data->max_lp_rate;563 564	drm_panel_add(&ddata->panel);565 566	r = mipi_dsi_attach(dsi);567	if (r < 0)568		goto err_dsi_attach;569 570	return 0;571 572err_dsi_attach:573	drm_panel_remove(&ddata->panel);574	sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group);575err_bl:576	if (ddata->extbldev)577		put_device(&ddata->extbldev->dev);578 579	return r;580}581 582static void dsicm_remove(struct mipi_dsi_device *dsi)583{584	struct panel_drv_data *ddata = mipi_dsi_get_drvdata(dsi);585 586	dev_dbg(&dsi->dev, "remove\n");587 588	mipi_dsi_detach(dsi);589 590	drm_panel_remove(&ddata->panel);591 592	sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group);593 594	if (ddata->extbldev)595		put_device(&ddata->extbldev->dev);596}597 598static const struct dsic_panel_data taal_data = {599	.xres = 864,600	.yres = 480,601	.refresh = 60,602	.width_mm = 0,603	.height_mm = 0,604	.max_hs_rate = 300000000,605	.max_lp_rate = 10000000,606	.te_support = true,607};608 609static const struct dsic_panel_data himalaya_data = {610	.xres = 480,611	.yres = 864,612	.refresh = 60,613	.width_mm = 49,614	.height_mm = 88,615	.max_hs_rate = 300000000,616	.max_lp_rate = 10000000,617	.te_support = false,618};619 620static const struct dsic_panel_data droid4_data = {621	.xres = 540,622	.yres = 960,623	.refresh = 60,624	.width_mm = 50,625	.height_mm = 89,626	.max_hs_rate = 300000000,627	.max_lp_rate = 10000000,628	.te_support = false,629};630 631static const struct of_device_id dsicm_of_match[] = {632	{ .compatible = "tpo,taal", .data = &taal_data },633	{ .compatible = "nokia,himalaya", &himalaya_data },634	{ .compatible = "motorola,droid4-panel", &droid4_data },635	{},636};637 638MODULE_DEVICE_TABLE(of, dsicm_of_match);639 640static struct mipi_dsi_driver dsicm_driver = {641	.probe = dsicm_probe,642	.remove = dsicm_remove,643	.driver = {644		.name = "panel-dsi-cm",645		.of_match_table = dsicm_of_match,646	},647};648module_mipi_dsi_driver(dsicm_driver);649 650MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");651MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");652MODULE_LICENSE("GPL");653