162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Generic DSI Command Mode panel driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013 Texas Instruments
662306a36Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/* #define DEBUG */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/backlight.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/fb.h>
1562306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/jiffies.h>
1862306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/sched/signal.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/workqueue.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <video/omapfb_dss.h>
2662306a36Sopenharmony_ci#include <video/mipi_display.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* DSI Virtual channel. Hardcoded for now. */
2962306a36Sopenharmony_ci#define TCH 0
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define DCS_READ_NUM_ERRORS	0x05
3262306a36Sopenharmony_ci#define DCS_BRIGHTNESS		0x51
3362306a36Sopenharmony_ci#define DCS_CTRL_DISPLAY	0x53
3462306a36Sopenharmony_ci#define DCS_GET_ID1		0xda
3562306a36Sopenharmony_ci#define DCS_GET_ID2		0xdb
3662306a36Sopenharmony_ci#define DCS_GET_ID3		0xdc
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct panel_drv_data {
3962306a36Sopenharmony_ci	struct omap_dss_device dssdev;
4062306a36Sopenharmony_ci	struct omap_dss_device *in;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	struct omap_video_timings timings;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	struct platform_device *pdev;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	struct mutex lock;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	struct backlight_device *bldev;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	unsigned long	hw_guard_end;	/* next value of jiffies when we can
5162306a36Sopenharmony_ci					 * issue the next sleep in/out command
5262306a36Sopenharmony_ci					 */
5362306a36Sopenharmony_ci	unsigned long	hw_guard_wait;	/* max guard time in jiffies */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* panel HW configuration from DT or platform data */
5662306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
5762306a36Sopenharmony_ci	struct gpio_desc *ext_te_gpio;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	bool use_dsi_backlight;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	struct omap_dsi_pin_config pin_config;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* runtime variables */
6462306a36Sopenharmony_ci	bool enabled;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	bool te_enabled;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	atomic_t do_update;
6962306a36Sopenharmony_ci	int channel;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	struct delayed_work te_timeout_work;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	bool intro_printed;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	bool ulps_enabled;
7662306a36Sopenharmony_ci	unsigned ulps_timeout;
7762306a36Sopenharmony_ci	struct delayed_work ulps_work;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic irqreturn_t dsicm_te_isr(int irq, void *data);
8362306a36Sopenharmony_cistatic void dsicm_te_timeout_work_callback(struct work_struct *work);
8462306a36Sopenharmony_cistatic int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int dsicm_panel_reset(struct panel_drv_data *ddata);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void dsicm_ulps_work(struct work_struct *work);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
9362306a36Sopenharmony_ci	ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void hw_guard_wait(struct panel_drv_data *ddata)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	unsigned long wait = ddata->hw_guard_end - jiffies;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if ((long)wait > 0 && time_before_eq(wait, ddata->hw_guard_wait)) {
10162306a36Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
10262306a36Sopenharmony_ci		schedule_timeout(wait);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
10962306a36Sopenharmony_ci	int r;
11062306a36Sopenharmony_ci	u8 buf[1];
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, buf, 1);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (r < 0)
11562306a36Sopenharmony_ci		return r;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	*data = buf[0];
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return 0;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
12562306a36Sopenharmony_ci	return in->ops.dsi->dcs_write(in, ddata->channel, &dcs_cmd, 1);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
13162306a36Sopenharmony_ci	u8 buf[2] = { dcs_cmd, param };
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return in->ops.dsi->dcs_write(in, ddata->channel, buf, 2);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int dsicm_sleep_in(struct panel_drv_data *ddata)
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
14062306a36Sopenharmony_ci	u8 cmd;
14162306a36Sopenharmony_ci	int r;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	hw_guard_wait(ddata);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	cmd = MIPI_DCS_ENTER_SLEEP_MODE;
14662306a36Sopenharmony_ci	r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, &cmd, 1);
14762306a36Sopenharmony_ci	if (r)
14862306a36Sopenharmony_ci		return r;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	hw_guard_start(ddata, 120);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	usleep_range(5000, 10000);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int dsicm_sleep_out(struct panel_drv_data *ddata)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	int r;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	hw_guard_wait(ddata);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	r = dsicm_dcs_write_0(ddata, MIPI_DCS_EXIT_SLEEP_MODE);
16462306a36Sopenharmony_ci	if (r)
16562306a36Sopenharmony_ci		return r;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	hw_guard_start(ddata, 120);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	usleep_range(5000, 10000);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	int r;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1);
17962306a36Sopenharmony_ci	if (r)
18062306a36Sopenharmony_ci		return r;
18162306a36Sopenharmony_ci	r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2);
18262306a36Sopenharmony_ci	if (r)
18362306a36Sopenharmony_ci		return r;
18462306a36Sopenharmony_ci	r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3);
18562306a36Sopenharmony_ci	if (r)
18662306a36Sopenharmony_ci		return r;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int dsicm_set_update_window(struct panel_drv_data *ddata,
19262306a36Sopenharmony_ci		u16 x, u16 y, u16 w, u16 h)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
19562306a36Sopenharmony_ci	int r;
19662306a36Sopenharmony_ci	u16 x1 = x;
19762306a36Sopenharmony_ci	u16 x2 = x + w - 1;
19862306a36Sopenharmony_ci	u16 y1 = y;
19962306a36Sopenharmony_ci	u16 y2 = y + h - 1;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	u8 buf[5];
20262306a36Sopenharmony_ci	buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
20362306a36Sopenharmony_ci	buf[1] = (x1 >> 8) & 0xff;
20462306a36Sopenharmony_ci	buf[2] = (x1 >> 0) & 0xff;
20562306a36Sopenharmony_ci	buf[3] = (x2 >> 8) & 0xff;
20662306a36Sopenharmony_ci	buf[4] = (x2 >> 0) & 0xff;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
20962306a36Sopenharmony_ci	if (r)
21062306a36Sopenharmony_ci		return r;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
21362306a36Sopenharmony_ci	buf[1] = (y1 >> 8) & 0xff;
21462306a36Sopenharmony_ci	buf[2] = (y1 >> 0) & 0xff;
21562306a36Sopenharmony_ci	buf[3] = (y2 >> 8) & 0xff;
21662306a36Sopenharmony_ci	buf[4] = (y2 >> 0) & 0xff;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
21962306a36Sopenharmony_ci	if (r)
22062306a36Sopenharmony_ci		return r;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	in->ops.dsi->bta_sync(in, ddata->channel);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return r;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void dsicm_queue_ulps_work(struct panel_drv_data *ddata)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	if (ddata->ulps_timeout > 0)
23062306a36Sopenharmony_ci		schedule_delayed_work(&ddata->ulps_work,
23162306a36Sopenharmony_ci				msecs_to_jiffies(ddata->ulps_timeout));
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic void dsicm_cancel_ulps_work(struct panel_drv_data *ddata)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	cancel_delayed_work(&ddata->ulps_work);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic int dsicm_enter_ulps(struct panel_drv_data *ddata)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
24262306a36Sopenharmony_ci	int r;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (ddata->ulps_enabled)
24562306a36Sopenharmony_ci		return 0;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	dsicm_cancel_ulps_work(ddata);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	r = _dsicm_enable_te(ddata, false);
25062306a36Sopenharmony_ci	if (r)
25162306a36Sopenharmony_ci		goto err;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (ddata->ext_te_gpio)
25462306a36Sopenharmony_ci		disable_irq(gpiod_to_irq(ddata->ext_te_gpio));
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	in->ops.dsi->disable(in, false, true);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	ddata->ulps_enabled = true;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cierr:
26362306a36Sopenharmony_ci	dev_err(&ddata->pdev->dev, "enter ULPS failed");
26462306a36Sopenharmony_ci	dsicm_panel_reset(ddata);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	ddata->ulps_enabled = false;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	dsicm_queue_ulps_work(ddata);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return r;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int dsicm_exit_ulps(struct panel_drv_data *ddata)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
27662306a36Sopenharmony_ci	int r;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (!ddata->ulps_enabled)
27962306a36Sopenharmony_ci		return 0;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	r = in->ops.dsi->enable(in);
28262306a36Sopenharmony_ci	if (r) {
28362306a36Sopenharmony_ci		dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
28462306a36Sopenharmony_ci		goto err1;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	in->ops.dsi->enable_hs(in, ddata->channel, true);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	r = _dsicm_enable_te(ddata, true);
29062306a36Sopenharmony_ci	if (r) {
29162306a36Sopenharmony_ci		dev_err(&ddata->pdev->dev, "failed to re-enable TE");
29262306a36Sopenharmony_ci		goto err2;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (ddata->ext_te_gpio)
29662306a36Sopenharmony_ci		enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	dsicm_queue_ulps_work(ddata);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	ddata->ulps_enabled = false;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cierr2:
30562306a36Sopenharmony_ci	dev_err(&ddata->pdev->dev, "failed to exit ULPS");
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	r = dsicm_panel_reset(ddata);
30862306a36Sopenharmony_ci	if (!r) {
30962306a36Sopenharmony_ci		if (ddata->ext_te_gpio)
31062306a36Sopenharmony_ci			enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
31162306a36Sopenharmony_ci		ddata->ulps_enabled = false;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_cierr1:
31462306a36Sopenharmony_ci	dsicm_queue_ulps_work(ddata);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return r;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int dsicm_wake_up(struct panel_drv_data *ddata)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	if (ddata->ulps_enabled)
32262306a36Sopenharmony_ci		return dsicm_exit_ulps(ddata);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	dsicm_cancel_ulps_work(ddata);
32562306a36Sopenharmony_ci	dsicm_queue_ulps_work(ddata);
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int dsicm_bl_update_status(struct backlight_device *dev)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
33262306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
33362306a36Sopenharmony_ci	int r;
33462306a36Sopenharmony_ci	int level = backlight_get_brightness(dev);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (ddata->enabled) {
34162306a36Sopenharmony_ci		in->ops.dsi->bus_lock(in);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		r = dsicm_wake_up(ddata);
34462306a36Sopenharmony_ci		if (!r)
34562306a36Sopenharmony_ci			r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		in->ops.dsi->bus_unlock(in);
34862306a36Sopenharmony_ci	} else {
34962306a36Sopenharmony_ci		r = 0;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return r;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int dsicm_bl_get_intensity(struct backlight_device *dev)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
36062306a36Sopenharmony_ci			dev->props.power == FB_BLANK_UNBLANK)
36162306a36Sopenharmony_ci		return dev->props.brightness;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return 0;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic const struct backlight_ops dsicm_bl_ops = {
36762306a36Sopenharmony_ci	.get_brightness = dsicm_bl_get_intensity,
36862306a36Sopenharmony_ci	.update_status  = dsicm_bl_update_status,
36962306a36Sopenharmony_ci};
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void dsicm_get_resolution(struct omap_dss_device *dssdev,
37262306a36Sopenharmony_ci		u16 *xres, u16 *yres)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	*xres = dssdev->panel.timings.x_res;
37562306a36Sopenharmony_ci	*yres = dssdev->panel.timings.y_res;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic ssize_t dsicm_num_errors_show(struct device *dev,
37962306a36Sopenharmony_ci		struct device_attribute *attr, char *buf)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct panel_drv_data *ddata = dev_get_drvdata(dev);
38262306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
38362306a36Sopenharmony_ci	u8 errors = 0;
38462306a36Sopenharmony_ci	int r;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (ddata->enabled) {
38962306a36Sopenharmony_ci		in->ops.dsi->bus_lock(in);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		r = dsicm_wake_up(ddata);
39262306a36Sopenharmony_ci		if (!r)
39362306a36Sopenharmony_ci			r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS,
39462306a36Sopenharmony_ci					&errors);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		in->ops.dsi->bus_unlock(in);
39762306a36Sopenharmony_ci	} else {
39862306a36Sopenharmony_ci		r = -ENODEV;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (r)
40462306a36Sopenharmony_ci		return r;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", errors);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic ssize_t dsicm_hw_revision_show(struct device *dev,
41062306a36Sopenharmony_ci		struct device_attribute *attr, char *buf)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct panel_drv_data *ddata = dev_get_drvdata(dev);
41362306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
41462306a36Sopenharmony_ci	u8 id1, id2, id3;
41562306a36Sopenharmony_ci	int r;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (ddata->enabled) {
42062306a36Sopenharmony_ci		in->ops.dsi->bus_lock(in);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		r = dsicm_wake_up(ddata);
42362306a36Sopenharmony_ci		if (!r)
42462306a36Sopenharmony_ci			r = dsicm_get_id(ddata, &id1, &id2, &id3);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		in->ops.dsi->bus_unlock(in);
42762306a36Sopenharmony_ci	} else {
42862306a36Sopenharmony_ci		r = -ENODEV;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (r)
43462306a36Sopenharmony_ci		return r;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return sysfs_emit(buf, "%02x.%02x.%02x\n", id1, id2, id3);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic ssize_t dsicm_store_ulps(struct device *dev,
44062306a36Sopenharmony_ci		struct device_attribute *attr,
44162306a36Sopenharmony_ci		const char *buf, size_t count)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct panel_drv_data *ddata = dev_get_drvdata(dev);
44462306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
44562306a36Sopenharmony_ci	unsigned long t;
44662306a36Sopenharmony_ci	int r;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	r = kstrtoul(buf, 0, &t);
44962306a36Sopenharmony_ci	if (r)
45062306a36Sopenharmony_ci		return r;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (ddata->enabled) {
45562306a36Sopenharmony_ci		in->ops.dsi->bus_lock(in);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		if (t)
45862306a36Sopenharmony_ci			r = dsicm_enter_ulps(ddata);
45962306a36Sopenharmony_ci		else
46062306a36Sopenharmony_ci			r = dsicm_wake_up(ddata);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		in->ops.dsi->bus_unlock(in);
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (r)
46862306a36Sopenharmony_ci		return r;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return count;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic ssize_t dsicm_show_ulps(struct device *dev,
47462306a36Sopenharmony_ci		struct device_attribute *attr,
47562306a36Sopenharmony_ci		char *buf)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct panel_drv_data *ddata = dev_get_drvdata(dev);
47862306a36Sopenharmony_ci	unsigned t;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
48162306a36Sopenharmony_ci	t = ddata->ulps_enabled;
48262306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", t);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic ssize_t dsicm_store_ulps_timeout(struct device *dev,
48862306a36Sopenharmony_ci		struct device_attribute *attr,
48962306a36Sopenharmony_ci		const char *buf, size_t count)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct panel_drv_data *ddata = dev_get_drvdata(dev);
49262306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
49362306a36Sopenharmony_ci	unsigned long t;
49462306a36Sopenharmony_ci	int r;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	r = kstrtoul(buf, 0, &t);
49762306a36Sopenharmony_ci	if (r)
49862306a36Sopenharmony_ci		return r;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
50162306a36Sopenharmony_ci	ddata->ulps_timeout = t;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (ddata->enabled) {
50462306a36Sopenharmony_ci		/* dsicm_wake_up will restart the timer */
50562306a36Sopenharmony_ci		in->ops.dsi->bus_lock(in);
50662306a36Sopenharmony_ci		r = dsicm_wake_up(ddata);
50762306a36Sopenharmony_ci		in->ops.dsi->bus_unlock(in);
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (r)
51362306a36Sopenharmony_ci		return r;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return count;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic ssize_t dsicm_show_ulps_timeout(struct device *dev,
51962306a36Sopenharmony_ci		struct device_attribute *attr,
52062306a36Sopenharmony_ci		char *buf)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct panel_drv_data *ddata = dev_get_drvdata(dev);
52362306a36Sopenharmony_ci	unsigned t;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
52662306a36Sopenharmony_ci	t = ddata->ulps_timeout;
52762306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", t);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL);
53362306a36Sopenharmony_cistatic DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL);
53462306a36Sopenharmony_cistatic DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
53562306a36Sopenharmony_ci		dsicm_show_ulps, dsicm_store_ulps);
53662306a36Sopenharmony_cistatic DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
53762306a36Sopenharmony_ci		dsicm_show_ulps_timeout, dsicm_store_ulps_timeout);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic struct attribute *dsicm_attrs[] = {
54062306a36Sopenharmony_ci	&dev_attr_num_dsi_errors.attr,
54162306a36Sopenharmony_ci	&dev_attr_hw_revision.attr,
54262306a36Sopenharmony_ci	&dev_attr_ulps.attr,
54362306a36Sopenharmony_ci	&dev_attr_ulps_timeout.attr,
54462306a36Sopenharmony_ci	NULL,
54562306a36Sopenharmony_ci};
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic const struct attribute_group dsicm_attr_group = {
54862306a36Sopenharmony_ci	.attrs = dsicm_attrs,
54962306a36Sopenharmony_ci};
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic void dsicm_hw_reset(struct panel_drv_data *ddata)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	/*
55462306a36Sopenharmony_ci	 * Note that we appear to activate the reset line here. However
55562306a36Sopenharmony_ci	 * existing DTSes specified incorrect polarity for it (active high),
55662306a36Sopenharmony_ci	 * so in fact this deasserts the reset line.
55762306a36Sopenharmony_ci	 */
55862306a36Sopenharmony_ci	gpiod_set_value_cansleep(ddata->reset_gpio, 1);
55962306a36Sopenharmony_ci	udelay(10);
56062306a36Sopenharmony_ci	/* reset the panel */
56162306a36Sopenharmony_ci	gpiod_set_value_cansleep(ddata->reset_gpio, 0);
56262306a36Sopenharmony_ci	/* keep reset asserted */
56362306a36Sopenharmony_ci	udelay(10);
56462306a36Sopenharmony_ci	/* release reset line */
56562306a36Sopenharmony_ci	gpiod_set_value_cansleep(ddata->reset_gpio, 1);
56662306a36Sopenharmony_ci	/* wait after releasing reset */
56762306a36Sopenharmony_ci	usleep_range(5000, 10000);
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic int dsicm_power_on(struct panel_drv_data *ddata)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
57362306a36Sopenharmony_ci	u8 id1, id2, id3;
57462306a36Sopenharmony_ci	int r;
57562306a36Sopenharmony_ci	struct omap_dss_dsi_config dsi_config = {
57662306a36Sopenharmony_ci		.mode = OMAP_DSS_DSI_CMD_MODE,
57762306a36Sopenharmony_ci		.pixel_format = OMAP_DSS_DSI_FMT_RGB888,
57862306a36Sopenharmony_ci		.timings = &ddata->timings,
57962306a36Sopenharmony_ci		.hs_clk_min = 150000000,
58062306a36Sopenharmony_ci		.hs_clk_max = 300000000,
58162306a36Sopenharmony_ci		.lp_clk_min = 7000000,
58262306a36Sopenharmony_ci		.lp_clk_max = 10000000,
58362306a36Sopenharmony_ci	};
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (ddata->pin_config.num_pins > 0) {
58662306a36Sopenharmony_ci		r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
58762306a36Sopenharmony_ci		if (r) {
58862306a36Sopenharmony_ci			dev_err(&ddata->pdev->dev,
58962306a36Sopenharmony_ci				"failed to configure DSI pins\n");
59062306a36Sopenharmony_ci			goto err0;
59162306a36Sopenharmony_ci		}
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	r = in->ops.dsi->set_config(in, &dsi_config);
59562306a36Sopenharmony_ci	if (r) {
59662306a36Sopenharmony_ci		dev_err(&ddata->pdev->dev, "failed to configure DSI\n");
59762306a36Sopenharmony_ci		goto err0;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	r = in->ops.dsi->enable(in);
60162306a36Sopenharmony_ci	if (r) {
60262306a36Sopenharmony_ci		dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
60362306a36Sopenharmony_ci		goto err0;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	dsicm_hw_reset(ddata);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	in->ops.dsi->enable_hs(in, ddata->channel, false);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	r = dsicm_sleep_out(ddata);
61162306a36Sopenharmony_ci	if (r)
61262306a36Sopenharmony_ci		goto err;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	r = dsicm_get_id(ddata, &id1, &id2, &id3);
61562306a36Sopenharmony_ci	if (r)
61662306a36Sopenharmony_ci		goto err;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff);
61962306a36Sopenharmony_ci	if (r)
62062306a36Sopenharmony_ci		goto err;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY,
62362306a36Sopenharmony_ci			(1<<2) | (1<<5));	/* BL | BCTRL */
62462306a36Sopenharmony_ci	if (r)
62562306a36Sopenharmony_ci		goto err;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT,
62862306a36Sopenharmony_ci		MIPI_DCS_PIXEL_FMT_24BIT);
62962306a36Sopenharmony_ci	if (r)
63062306a36Sopenharmony_ci		goto err;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON);
63362306a36Sopenharmony_ci	if (r)
63462306a36Sopenharmony_ci		goto err;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	r = _dsicm_enable_te(ddata, ddata->te_enabled);
63762306a36Sopenharmony_ci	if (r)
63862306a36Sopenharmony_ci		goto err;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	r = in->ops.dsi->enable_video_output(in, ddata->channel);
64162306a36Sopenharmony_ci	if (r)
64262306a36Sopenharmony_ci		goto err;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	ddata->enabled = 1;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (!ddata->intro_printed) {
64762306a36Sopenharmony_ci		dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n",
64862306a36Sopenharmony_ci			id1, id2, id3);
64962306a36Sopenharmony_ci		ddata->intro_printed = true;
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	in->ops.dsi->enable_hs(in, ddata->channel, true);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	return 0;
65562306a36Sopenharmony_cierr:
65662306a36Sopenharmony_ci	dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n");
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	dsicm_hw_reset(ddata);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	in->ops.dsi->disable(in, true, false);
66162306a36Sopenharmony_cierr0:
66262306a36Sopenharmony_ci	return r;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic void dsicm_power_off(struct panel_drv_data *ddata)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
66862306a36Sopenharmony_ci	int r;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	in->ops.dsi->disable_video_output(in, ddata->channel);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF);
67362306a36Sopenharmony_ci	if (!r)
67462306a36Sopenharmony_ci		r = dsicm_sleep_in(ddata);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (r) {
67762306a36Sopenharmony_ci		dev_err(&ddata->pdev->dev,
67862306a36Sopenharmony_ci				"error disabling panel, issuing HW reset\n");
67962306a36Sopenharmony_ci		dsicm_hw_reset(ddata);
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	in->ops.dsi->disable(in, true, false);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	ddata->enabled = 0;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic int dsicm_panel_reset(struct panel_drv_data *ddata)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	dev_err(&ddata->pdev->dev, "performing LCD reset\n");
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	dsicm_power_off(ddata);
69262306a36Sopenharmony_ci	dsicm_hw_reset(ddata);
69362306a36Sopenharmony_ci	return dsicm_power_on(ddata);
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic int dsicm_connect(struct omap_dss_device *dssdev)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	struct panel_drv_data *ddata = to_panel_data(dssdev);
69962306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
70062306a36Sopenharmony_ci	struct device *dev = &ddata->pdev->dev;
70162306a36Sopenharmony_ci	int r;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (omapdss_device_is_connected(dssdev))
70462306a36Sopenharmony_ci		return 0;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	r = in->ops.dsi->connect(in, dssdev);
70762306a36Sopenharmony_ci	if (r) {
70862306a36Sopenharmony_ci		dev_err(dev, "Failed to connect to video source\n");
70962306a36Sopenharmony_ci		return r;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	r = in->ops.dsi->request_vc(ddata->in, &ddata->channel);
71362306a36Sopenharmony_ci	if (r) {
71462306a36Sopenharmony_ci		dev_err(dev, "failed to get virtual channel\n");
71562306a36Sopenharmony_ci		goto err_req_vc;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH);
71962306a36Sopenharmony_ci	if (r) {
72062306a36Sopenharmony_ci		dev_err(dev, "failed to set VC_ID\n");
72162306a36Sopenharmony_ci		goto err_vc_id;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	return 0;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cierr_vc_id:
72762306a36Sopenharmony_ci	in->ops.dsi->release_vc(ddata->in, ddata->channel);
72862306a36Sopenharmony_cierr_req_vc:
72962306a36Sopenharmony_ci	in->ops.dsi->disconnect(in, dssdev);
73062306a36Sopenharmony_ci	return r;
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cistatic void dsicm_disconnect(struct omap_dss_device *dssdev)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct panel_drv_data *ddata = to_panel_data(dssdev);
73662306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (!omapdss_device_is_connected(dssdev))
73962306a36Sopenharmony_ci		return;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	in->ops.dsi->release_vc(in, ddata->channel);
74262306a36Sopenharmony_ci	in->ops.dsi->disconnect(in, dssdev);
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic int dsicm_enable(struct omap_dss_device *dssdev)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct panel_drv_data *ddata = to_panel_data(dssdev);
74862306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
74962306a36Sopenharmony_ci	int r;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	dev_dbg(&ddata->pdev->dev, "enable\n");
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	if (!omapdss_device_is_connected(dssdev)) {
75662306a36Sopenharmony_ci		r = -ENODEV;
75762306a36Sopenharmony_ci		goto err;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	if (omapdss_device_is_enabled(dssdev)) {
76162306a36Sopenharmony_ci		r = 0;
76262306a36Sopenharmony_ci		goto err;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	in->ops.dsi->bus_lock(in);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	r = dsicm_power_on(ddata);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (r)
77262306a36Sopenharmony_ci		goto err;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	return 0;
77962306a36Sopenharmony_cierr:
78062306a36Sopenharmony_ci	dev_dbg(&ddata->pdev->dev, "enable failed\n");
78162306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
78262306a36Sopenharmony_ci	return r;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void dsicm_disable(struct omap_dss_device *dssdev)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct panel_drv_data *ddata = to_panel_data(dssdev);
78862306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
78962306a36Sopenharmony_ci	int r;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	dev_dbg(&ddata->pdev->dev, "disable\n");
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	dsicm_cancel_ulps_work(ddata);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	in->ops.dsi->bus_lock(in);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (omapdss_device_is_enabled(dssdev)) {
80062306a36Sopenharmony_ci		r = dsicm_wake_up(ddata);
80162306a36Sopenharmony_ci		if (!r)
80262306a36Sopenharmony_ci			dsicm_power_off(ddata);
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic void dsicm_framedone_cb(int err, void *data)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct panel_drv_data *ddata = data;
81562306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err);
81862306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(ddata->in);
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic irqreturn_t dsicm_te_isr(int irq, void *data)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct panel_drv_data *ddata = data;
82462306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
82562306a36Sopenharmony_ci	int old;
82662306a36Sopenharmony_ci	int r;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	old = atomic_cmpxchg(&ddata->do_update, 1, 0);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (old) {
83162306a36Sopenharmony_ci		cancel_delayed_work(&ddata->te_timeout_work);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
83462306a36Sopenharmony_ci				ddata);
83562306a36Sopenharmony_ci		if (r)
83662306a36Sopenharmony_ci			goto err;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return IRQ_HANDLED;
84062306a36Sopenharmony_cierr:
84162306a36Sopenharmony_ci	dev_err(&ddata->pdev->dev, "start update failed\n");
84262306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
84362306a36Sopenharmony_ci	return IRQ_HANDLED;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void dsicm_te_timeout_work_callback(struct work_struct *work)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
84962306a36Sopenharmony_ci					te_timeout_work.work);
85062306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n");
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	atomic_set(&ddata->do_update, 0);
85562306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic int dsicm_update(struct omap_dss_device *dssdev,
85962306a36Sopenharmony_ci				    u16 x, u16 y, u16 w, u16 h)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct panel_drv_data *ddata = to_panel_data(dssdev);
86262306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
86362306a36Sopenharmony_ci	int r;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
86862306a36Sopenharmony_ci	in->ops.dsi->bus_lock(in);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	r = dsicm_wake_up(ddata);
87162306a36Sopenharmony_ci	if (r)
87262306a36Sopenharmony_ci		goto err;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (!ddata->enabled) {
87562306a36Sopenharmony_ci		r = 0;
87662306a36Sopenharmony_ci		goto err;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	/* XXX no need to send this every frame, but dsi break if not done */
88062306a36Sopenharmony_ci	r = dsicm_set_update_window(ddata, 0, 0,
88162306a36Sopenharmony_ci			dssdev->panel.timings.x_res,
88262306a36Sopenharmony_ci			dssdev->panel.timings.y_res);
88362306a36Sopenharmony_ci	if (r)
88462306a36Sopenharmony_ci		goto err;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (ddata->te_enabled && ddata->ext_te_gpio) {
88762306a36Sopenharmony_ci		schedule_delayed_work(&ddata->te_timeout_work,
88862306a36Sopenharmony_ci				msecs_to_jiffies(250));
88962306a36Sopenharmony_ci		atomic_set(&ddata->do_update, 1);
89062306a36Sopenharmony_ci	} else {
89162306a36Sopenharmony_ci		r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
89262306a36Sopenharmony_ci				ddata);
89362306a36Sopenharmony_ci		if (r)
89462306a36Sopenharmony_ci			goto err;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* note: no bus_unlock here. unlock is in framedone_cb */
89862306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
89962306a36Sopenharmony_ci	return 0;
90062306a36Sopenharmony_cierr:
90162306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
90262306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
90362306a36Sopenharmony_ci	return r;
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cistatic int dsicm_sync(struct omap_dss_device *dssdev)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	struct panel_drv_data *ddata = to_panel_data(dssdev);
90962306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	dev_dbg(&ddata->pdev->dev, "sync\n");
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
91462306a36Sopenharmony_ci	in->ops.dsi->bus_lock(in);
91562306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
91662306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	dev_dbg(&ddata->pdev->dev, "sync done\n");
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	return 0;
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
92662306a36Sopenharmony_ci	int r;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (enable)
92962306a36Sopenharmony_ci		r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0);
93062306a36Sopenharmony_ci	else
93162306a36Sopenharmony_ci		r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (!ddata->ext_te_gpio)
93462306a36Sopenharmony_ci		in->ops.dsi->enable_te(in, enable);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/* possible panel bug */
93762306a36Sopenharmony_ci	msleep(100);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	return r;
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct panel_drv_data *ddata = to_panel_data(dssdev);
94562306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
94662306a36Sopenharmony_ci	int r;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (ddata->te_enabled == enable)
95162306a36Sopenharmony_ci		goto end;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	in->ops.dsi->bus_lock(in);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (ddata->enabled) {
95662306a36Sopenharmony_ci		r = dsicm_wake_up(ddata);
95762306a36Sopenharmony_ci		if (r)
95862306a36Sopenharmony_ci			goto err;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci		r = _dsicm_enable_te(ddata, enable);
96162306a36Sopenharmony_ci		if (r)
96262306a36Sopenharmony_ci			goto err;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	ddata->te_enabled = enable;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
96862306a36Sopenharmony_ciend:
96962306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	return 0;
97262306a36Sopenharmony_cierr:
97362306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
97462306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	return r;
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistatic int dsicm_get_te(struct omap_dss_device *dssdev)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	struct panel_drv_data *ddata = to_panel_data(dssdev);
98262306a36Sopenharmony_ci	int r;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
98562306a36Sopenharmony_ci	r = ddata->te_enabled;
98662306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	return r;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic int dsicm_memory_read(struct omap_dss_device *dssdev,
99262306a36Sopenharmony_ci		void *buf, size_t size,
99362306a36Sopenharmony_ci		u16 x, u16 y, u16 w, u16 h)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct panel_drv_data *ddata = to_panel_data(dssdev);
99662306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
99762306a36Sopenharmony_ci	int r;
99862306a36Sopenharmony_ci	int first = 1;
99962306a36Sopenharmony_ci	int plen;
100062306a36Sopenharmony_ci	unsigned buf_used = 0;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (size < w * h * 3)
100362306a36Sopenharmony_ci		return -ENOMEM;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (!ddata->enabled) {
100862306a36Sopenharmony_ci		r = -ENODEV;
100962306a36Sopenharmony_ci		goto err1;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	size = min(w * h * 3,
101362306a36Sopenharmony_ci			dssdev->panel.timings.x_res *
101462306a36Sopenharmony_ci			dssdev->panel.timings.y_res * 3);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	in->ops.dsi->bus_lock(in);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	r = dsicm_wake_up(ddata);
101962306a36Sopenharmony_ci	if (r)
102062306a36Sopenharmony_ci		goto err2;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	/* plen 1 or 2 goes into short packet. until checksum error is fixed,
102362306a36Sopenharmony_ci	 * use short packets. plen 32 works, but bigger packets seem to cause
102462306a36Sopenharmony_ci	 * an error. */
102562306a36Sopenharmony_ci	if (size % 2)
102662306a36Sopenharmony_ci		plen = 1;
102762306a36Sopenharmony_ci	else
102862306a36Sopenharmony_ci		plen = 2;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	dsicm_set_update_window(ddata, x, y, w, h);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen);
103362306a36Sopenharmony_ci	if (r)
103462306a36Sopenharmony_ci		goto err2;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	while (buf_used < size) {
103762306a36Sopenharmony_ci		u8 dcs_cmd = first ? 0x2e : 0x3e;
103862306a36Sopenharmony_ci		first = 0;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd,
104162306a36Sopenharmony_ci				buf + buf_used, size - buf_used);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci		if (r < 0) {
104462306a36Sopenharmony_ci			dev_err(dssdev->dev, "read error\n");
104562306a36Sopenharmony_ci			goto err3;
104662306a36Sopenharmony_ci		}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci		buf_used += r;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		if (r < plen) {
105162306a36Sopenharmony_ci			dev_err(&ddata->pdev->dev, "short read\n");
105262306a36Sopenharmony_ci			break;
105362306a36Sopenharmony_ci		}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci		if (signal_pending(current)) {
105662306a36Sopenharmony_ci			dev_err(&ddata->pdev->dev, "signal pending, "
105762306a36Sopenharmony_ci					"aborting memory read\n");
105862306a36Sopenharmony_ci			r = -ERESTARTSYS;
105962306a36Sopenharmony_ci			goto err3;
106062306a36Sopenharmony_ci		}
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	r = buf_used;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cierr3:
106662306a36Sopenharmony_ci	in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1);
106762306a36Sopenharmony_cierr2:
106862306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
106962306a36Sopenharmony_cierr1:
107062306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
107162306a36Sopenharmony_ci	return r;
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic void dsicm_ulps_work(struct work_struct *work)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
107762306a36Sopenharmony_ci			ulps_work.work);
107862306a36Sopenharmony_ci	struct omap_dss_device *dssdev = &ddata->dssdev;
107962306a36Sopenharmony_ci	struct omap_dss_device *in = ddata->in;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	mutex_lock(&ddata->lock);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) {
108462306a36Sopenharmony_ci		mutex_unlock(&ddata->lock);
108562306a36Sopenharmony_ci		return;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	in->ops.dsi->bus_lock(in);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	dsicm_enter_ulps(ddata);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	in->ops.dsi->bus_unlock(in);
109362306a36Sopenharmony_ci	mutex_unlock(&ddata->lock);
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cistatic struct omap_dss_driver dsicm_ops = {
109762306a36Sopenharmony_ci	.connect	= dsicm_connect,
109862306a36Sopenharmony_ci	.disconnect	= dsicm_disconnect,
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	.enable		= dsicm_enable,
110162306a36Sopenharmony_ci	.disable	= dsicm_disable,
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	.update		= dsicm_update,
110462306a36Sopenharmony_ci	.sync		= dsicm_sync,
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	.get_resolution	= dsicm_get_resolution,
110762306a36Sopenharmony_ci	.get_recommended_bpp = omapdss_default_get_recommended_bpp,
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	.enable_te	= dsicm_enable_te,
111062306a36Sopenharmony_ci	.get_te		= dsicm_get_te,
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	.memory_read	= dsicm_memory_read,
111362306a36Sopenharmony_ci};
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_cistatic int dsicm_probe(struct platform_device *pdev)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	struct backlight_properties props;
111862306a36Sopenharmony_ci	struct panel_drv_data *ddata;
111962306a36Sopenharmony_ci	struct backlight_device *bldev = NULL;
112062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
112162306a36Sopenharmony_ci	struct omap_dss_device *dssdev;
112262306a36Sopenharmony_ci	int r;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	dev_dbg(dev, "probe\n");
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	if (!pdev->dev.of_node)
112762306a36Sopenharmony_ci		return -ENODEV;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
113062306a36Sopenharmony_ci	if (!ddata)
113162306a36Sopenharmony_ci		return -ENOMEM;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	platform_set_drvdata(pdev, ddata);
113462306a36Sopenharmony_ci	ddata->pdev = pdev;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	ddata->in = omapdss_of_find_source_for_first_ep(pdev->dev.of_node);
113762306a36Sopenharmony_ci	r = PTR_ERR_OR_ZERO(ddata->in);
113862306a36Sopenharmony_ci	if (r) {
113962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to find video source: %d\n", r);
114062306a36Sopenharmony_ci		return r;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	ddata->timings.x_res = 864;
114462306a36Sopenharmony_ci	ddata->timings.y_res = 480;
114562306a36Sopenharmony_ci	ddata->timings.pixelclock = 864 * 480 * 60;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	dssdev = &ddata->dssdev;
114862306a36Sopenharmony_ci	dssdev->dev = dev;
114962306a36Sopenharmony_ci	dssdev->driver = &dsicm_ops;
115062306a36Sopenharmony_ci	dssdev->panel.timings = ddata->timings;
115162306a36Sopenharmony_ci	dssdev->type = OMAP_DISPLAY_TYPE_DSI;
115262306a36Sopenharmony_ci	dssdev->owner = THIS_MODULE;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
115562306a36Sopenharmony_ci	dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
115662306a36Sopenharmony_ci		OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	r = omapdss_register_display(dssdev);
115962306a36Sopenharmony_ci	if (r) {
116062306a36Sopenharmony_ci		dev_err(dev, "Failed to register panel\n");
116162306a36Sopenharmony_ci		goto err_reg;
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	mutex_init(&ddata->lock);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	atomic_set(&ddata->do_update, 0);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
116962306a36Sopenharmony_ci	r = PTR_ERR_OR_ZERO(ddata->reset_gpio);
117062306a36Sopenharmony_ci	if (r) {
117162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to request reset gpio: %d\n", r);
117262306a36Sopenharmony_ci		return r;
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	gpiod_set_consumer_name(ddata->reset_gpio, "taal rst");
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	ddata->ext_te_gpio = devm_gpiod_get_optional(&pdev->dev, "te",
117862306a36Sopenharmony_ci						     GPIOD_IN);
117962306a36Sopenharmony_ci	r = PTR_ERR_OR_ZERO(ddata->ext_te_gpio);
118062306a36Sopenharmony_ci	if (r) {
118162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to request TE gpio: %d\n", r);
118262306a36Sopenharmony_ci		return r;
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (ddata->ext_te_gpio) {
118662306a36Sopenharmony_ci		gpiod_set_consumer_name(ddata->ext_te_gpio, "taal irq");
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		r = devm_request_irq(dev, gpiod_to_irq(ddata->ext_te_gpio),
118962306a36Sopenharmony_ci				dsicm_te_isr,
119062306a36Sopenharmony_ci				IRQF_TRIGGER_RISING,
119162306a36Sopenharmony_ci				"taal vsync", ddata);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci		if (r) {
119462306a36Sopenharmony_ci			dev_err(dev, "IRQ request failed\n");
119562306a36Sopenharmony_ci			return r;
119662306a36Sopenharmony_ci		}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci		INIT_DEFERRABLE_WORK(&ddata->te_timeout_work,
119962306a36Sopenharmony_ci					dsicm_te_timeout_work_callback);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		dev_dbg(dev, "Using GPIO TE\n");
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	dsicm_hw_reset(ddata);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (ddata->use_dsi_backlight) {
120962306a36Sopenharmony_ci		memset(&props, 0, sizeof(struct backlight_properties));
121062306a36Sopenharmony_ci		props.max_brightness = 255;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci		props.type = BACKLIGHT_RAW;
121362306a36Sopenharmony_ci		bldev = backlight_device_register(dev_name(dev),
121462306a36Sopenharmony_ci				dev, ddata, &dsicm_bl_ops, &props);
121562306a36Sopenharmony_ci		if (IS_ERR(bldev)) {
121662306a36Sopenharmony_ci			r = PTR_ERR(bldev);
121762306a36Sopenharmony_ci			goto err_reg;
121862306a36Sopenharmony_ci		}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		ddata->bldev = bldev;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		bldev->props.fb_blank = FB_BLANK_UNBLANK;
122362306a36Sopenharmony_ci		bldev->props.power = FB_BLANK_UNBLANK;
122462306a36Sopenharmony_ci		bldev->props.brightness = 255;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci		dsicm_bl_update_status(bldev);
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
123062306a36Sopenharmony_ci	if (r) {
123162306a36Sopenharmony_ci		dev_err(dev, "failed to create sysfs files\n");
123262306a36Sopenharmony_ci		goto err_sysfs_create;
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	return 0;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cierr_sysfs_create:
123862306a36Sopenharmony_ci	if (bldev != NULL)
123962306a36Sopenharmony_ci		backlight_device_unregister(bldev);
124062306a36Sopenharmony_cierr_reg:
124162306a36Sopenharmony_ci	return r;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic int __exit dsicm_remove(struct platform_device *pdev)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
124762306a36Sopenharmony_ci	struct omap_dss_device *dssdev = &ddata->dssdev;
124862306a36Sopenharmony_ci	struct backlight_device *bldev;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "remove\n");
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	omapdss_unregister_display(dssdev);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	dsicm_disable(dssdev);
125562306a36Sopenharmony_ci	dsicm_disconnect(dssdev);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	bldev = ddata->bldev;
126062306a36Sopenharmony_ci	if (bldev != NULL) {
126162306a36Sopenharmony_ci		bldev->props.power = FB_BLANK_POWERDOWN;
126262306a36Sopenharmony_ci		dsicm_bl_update_status(bldev);
126362306a36Sopenharmony_ci		backlight_device_unregister(bldev);
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	omap_dss_put_device(ddata->in);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	dsicm_cancel_ulps_work(ddata);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	/* reset, to be sure that the panel is in a valid state */
127162306a36Sopenharmony_ci	dsicm_hw_reset(ddata);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	return 0;
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic const struct of_device_id dsicm_of_match[] = {
127762306a36Sopenharmony_ci	{ .compatible = "omapdss,panel-dsi-cm", },
127862306a36Sopenharmony_ci	{},
127962306a36Sopenharmony_ci};
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dsicm_of_match);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_cistatic struct platform_driver dsicm_driver = {
128462306a36Sopenharmony_ci	.probe = dsicm_probe,
128562306a36Sopenharmony_ci	.remove = __exit_p(dsicm_remove),
128662306a36Sopenharmony_ci	.driver = {
128762306a36Sopenharmony_ci		.name = "panel-dsi-cm",
128862306a36Sopenharmony_ci		.of_match_table = dsicm_of_match,
128962306a36Sopenharmony_ci		.suppress_bind_attrs = true,
129062306a36Sopenharmony_ci	},
129162306a36Sopenharmony_ci};
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_cimodule_platform_driver(dsicm_driver);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ciMODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
129662306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");
129762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1298