1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f76ee892STomi Valkeinen /*
3f76ee892STomi Valkeinen * Generic DSI Command Mode panel driver
4f76ee892STomi Valkeinen *
5f76ee892STomi Valkeinen * Copyright (C) 2013 Texas Instruments
6f76ee892STomi Valkeinen * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
7f76ee892STomi Valkeinen */
8f76ee892STomi Valkeinen
9f76ee892STomi Valkeinen /* #define DEBUG */
10f76ee892STomi Valkeinen
11f76ee892STomi Valkeinen #include <linux/backlight.h>
12f76ee892STomi Valkeinen #include <linux/delay.h>
1367c366deSDmitry Torokhov #include <linux/err.h>
14f76ee892STomi Valkeinen #include <linux/fb.h>
1567c366deSDmitry Torokhov #include <linux/gpio/consumer.h>
16f76ee892STomi Valkeinen #include <linux/interrupt.h>
17f76ee892STomi Valkeinen #include <linux/jiffies.h>
18*e8812acbSRob Herring #include <linux/mod_devicetable.h>
19f76ee892STomi Valkeinen #include <linux/module.h>
20f76ee892STomi Valkeinen #include <linux/platform_device.h>
21174cd4b1SIngo Molnar #include <linux/sched/signal.h>
22f76ee892STomi Valkeinen #include <linux/slab.h>
23f76ee892STomi Valkeinen #include <linux/workqueue.h>
24f76ee892STomi Valkeinen
2562d9e44eSPeter Ujfalusi #include <video/omapfb_dss.h>
26f76ee892STomi Valkeinen #include <video/mipi_display.h>
27f76ee892STomi Valkeinen
28f76ee892STomi Valkeinen /* DSI Virtual channel. Hardcoded for now. */
29f76ee892STomi Valkeinen #define TCH 0
30f76ee892STomi Valkeinen
31f76ee892STomi Valkeinen #define DCS_READ_NUM_ERRORS 0x05
32f76ee892STomi Valkeinen #define DCS_BRIGHTNESS 0x51
33f76ee892STomi Valkeinen #define DCS_CTRL_DISPLAY 0x53
34f76ee892STomi Valkeinen #define DCS_GET_ID1 0xda
35f76ee892STomi Valkeinen #define DCS_GET_ID2 0xdb
36f76ee892STomi Valkeinen #define DCS_GET_ID3 0xdc
37f76ee892STomi Valkeinen
38f76ee892STomi Valkeinen struct panel_drv_data {
39f76ee892STomi Valkeinen struct omap_dss_device dssdev;
40f76ee892STomi Valkeinen struct omap_dss_device *in;
41f76ee892STomi Valkeinen
42f76ee892STomi Valkeinen struct omap_video_timings timings;
43f76ee892STomi Valkeinen
44f76ee892STomi Valkeinen struct platform_device *pdev;
45f76ee892STomi Valkeinen
46f76ee892STomi Valkeinen struct mutex lock;
47f76ee892STomi Valkeinen
48f76ee892STomi Valkeinen struct backlight_device *bldev;
49f76ee892STomi Valkeinen
50f76ee892STomi Valkeinen unsigned long hw_guard_end; /* next value of jiffies when we can
51f76ee892STomi Valkeinen * issue the next sleep in/out command
52f76ee892STomi Valkeinen */
53f76ee892STomi Valkeinen unsigned long hw_guard_wait; /* max guard time in jiffies */
54f76ee892STomi Valkeinen
55f76ee892STomi Valkeinen /* panel HW configuration from DT or platform data */
5667c366deSDmitry Torokhov struct gpio_desc *reset_gpio;
5767c366deSDmitry Torokhov struct gpio_desc *ext_te_gpio;
58f76ee892STomi Valkeinen
59f76ee892STomi Valkeinen bool use_dsi_backlight;
60f76ee892STomi Valkeinen
61f76ee892STomi Valkeinen struct omap_dsi_pin_config pin_config;
62f76ee892STomi Valkeinen
63f76ee892STomi Valkeinen /* runtime variables */
64f76ee892STomi Valkeinen bool enabled;
65f76ee892STomi Valkeinen
66f76ee892STomi Valkeinen bool te_enabled;
67f76ee892STomi Valkeinen
68f76ee892STomi Valkeinen atomic_t do_update;
69f76ee892STomi Valkeinen int channel;
70f76ee892STomi Valkeinen
71f76ee892STomi Valkeinen struct delayed_work te_timeout_work;
72f76ee892STomi Valkeinen
73f76ee892STomi Valkeinen bool intro_printed;
74f76ee892STomi Valkeinen
75f76ee892STomi Valkeinen bool ulps_enabled;
76f76ee892STomi Valkeinen unsigned ulps_timeout;
77f76ee892STomi Valkeinen struct delayed_work ulps_work;
78f76ee892STomi Valkeinen };
79f76ee892STomi Valkeinen
80f76ee892STomi Valkeinen #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
81f76ee892STomi Valkeinen
82f76ee892STomi Valkeinen static irqreturn_t dsicm_te_isr(int irq, void *data);
83f76ee892STomi Valkeinen static void dsicm_te_timeout_work_callback(struct work_struct *work);
84f76ee892STomi Valkeinen static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable);
85f76ee892STomi Valkeinen
86f76ee892STomi Valkeinen static int dsicm_panel_reset(struct panel_drv_data *ddata);
87f76ee892STomi Valkeinen
88f76ee892STomi Valkeinen static void dsicm_ulps_work(struct work_struct *work);
89f76ee892STomi Valkeinen
hw_guard_start(struct panel_drv_data * ddata,int guard_msec)90f76ee892STomi Valkeinen static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
91f76ee892STomi Valkeinen {
92f76ee892STomi Valkeinen ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
93f76ee892STomi Valkeinen ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
94f76ee892STomi Valkeinen }
95f76ee892STomi Valkeinen
hw_guard_wait(struct panel_drv_data * ddata)96f76ee892STomi Valkeinen static void hw_guard_wait(struct panel_drv_data *ddata)
97f76ee892STomi Valkeinen {
98f76ee892STomi Valkeinen unsigned long wait = ddata->hw_guard_end - jiffies;
99f76ee892STomi Valkeinen
100ac5b52d2SKarim Eshapa if ((long)wait > 0 && time_before_eq(wait, ddata->hw_guard_wait)) {
101f76ee892STomi Valkeinen set_current_state(TASK_UNINTERRUPTIBLE);
102f76ee892STomi Valkeinen schedule_timeout(wait);
103f76ee892STomi Valkeinen }
104f76ee892STomi Valkeinen }
105f76ee892STomi Valkeinen
dsicm_dcs_read_1(struct panel_drv_data * ddata,u8 dcs_cmd,u8 * data)106f76ee892STomi Valkeinen static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
107f76ee892STomi Valkeinen {
108f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
109f76ee892STomi Valkeinen int r;
110f76ee892STomi Valkeinen u8 buf[1];
111f76ee892STomi Valkeinen
112f76ee892STomi Valkeinen r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, buf, 1);
113f76ee892STomi Valkeinen
114f76ee892STomi Valkeinen if (r < 0)
115f76ee892STomi Valkeinen return r;
116f76ee892STomi Valkeinen
117f76ee892STomi Valkeinen *data = buf[0];
118f76ee892STomi Valkeinen
119f76ee892STomi Valkeinen return 0;
120f76ee892STomi Valkeinen }
121f76ee892STomi Valkeinen
dsicm_dcs_write_0(struct panel_drv_data * ddata,u8 dcs_cmd)122f76ee892STomi Valkeinen static int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd)
123f76ee892STomi Valkeinen {
124f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
125f76ee892STomi Valkeinen return in->ops.dsi->dcs_write(in, ddata->channel, &dcs_cmd, 1);
126f76ee892STomi Valkeinen }
127f76ee892STomi Valkeinen
dsicm_dcs_write_1(struct panel_drv_data * ddata,u8 dcs_cmd,u8 param)128f76ee892STomi Valkeinen static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
129f76ee892STomi Valkeinen {
130f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
131f76ee892STomi Valkeinen u8 buf[2] = { dcs_cmd, param };
132f76ee892STomi Valkeinen
133f76ee892STomi Valkeinen return in->ops.dsi->dcs_write(in, ddata->channel, buf, 2);
134f76ee892STomi Valkeinen }
135f76ee892STomi Valkeinen
dsicm_sleep_in(struct panel_drv_data * ddata)136f76ee892STomi Valkeinen static int dsicm_sleep_in(struct panel_drv_data *ddata)
137f76ee892STomi Valkeinen
138f76ee892STomi Valkeinen {
139f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
140f76ee892STomi Valkeinen u8 cmd;
141f76ee892STomi Valkeinen int r;
142f76ee892STomi Valkeinen
143f76ee892STomi Valkeinen hw_guard_wait(ddata);
144f76ee892STomi Valkeinen
145f76ee892STomi Valkeinen cmd = MIPI_DCS_ENTER_SLEEP_MODE;
146f76ee892STomi Valkeinen r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, &cmd, 1);
147f76ee892STomi Valkeinen if (r)
148f76ee892STomi Valkeinen return r;
149f76ee892STomi Valkeinen
150f76ee892STomi Valkeinen hw_guard_start(ddata, 120);
151f76ee892STomi Valkeinen
152f76ee892STomi Valkeinen usleep_range(5000, 10000);
153f76ee892STomi Valkeinen
154f76ee892STomi Valkeinen return 0;
155f76ee892STomi Valkeinen }
156f76ee892STomi Valkeinen
dsicm_sleep_out(struct panel_drv_data * ddata)157f76ee892STomi Valkeinen static int dsicm_sleep_out(struct panel_drv_data *ddata)
158f76ee892STomi Valkeinen {
159f76ee892STomi Valkeinen int r;
160f76ee892STomi Valkeinen
161f76ee892STomi Valkeinen hw_guard_wait(ddata);
162f76ee892STomi Valkeinen
163f76ee892STomi Valkeinen r = dsicm_dcs_write_0(ddata, MIPI_DCS_EXIT_SLEEP_MODE);
164f76ee892STomi Valkeinen if (r)
165f76ee892STomi Valkeinen return r;
166f76ee892STomi Valkeinen
167f76ee892STomi Valkeinen hw_guard_start(ddata, 120);
168f76ee892STomi Valkeinen
169f76ee892STomi Valkeinen usleep_range(5000, 10000);
170f76ee892STomi Valkeinen
171f76ee892STomi Valkeinen return 0;
172f76ee892STomi Valkeinen }
173f76ee892STomi Valkeinen
dsicm_get_id(struct panel_drv_data * ddata,u8 * id1,u8 * id2,u8 * id3)174f76ee892STomi Valkeinen static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)
175f76ee892STomi Valkeinen {
176f76ee892STomi Valkeinen int r;
177f76ee892STomi Valkeinen
178f76ee892STomi Valkeinen r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1);
179f76ee892STomi Valkeinen if (r)
180f76ee892STomi Valkeinen return r;
181f76ee892STomi Valkeinen r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2);
182f76ee892STomi Valkeinen if (r)
183f76ee892STomi Valkeinen return r;
184f76ee892STomi Valkeinen r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3);
185f76ee892STomi Valkeinen if (r)
186f76ee892STomi Valkeinen return r;
187f76ee892STomi Valkeinen
188f76ee892STomi Valkeinen return 0;
189f76ee892STomi Valkeinen }
190f76ee892STomi Valkeinen
dsicm_set_update_window(struct panel_drv_data * ddata,u16 x,u16 y,u16 w,u16 h)191f76ee892STomi Valkeinen static int dsicm_set_update_window(struct panel_drv_data *ddata,
192f76ee892STomi Valkeinen u16 x, u16 y, u16 w, u16 h)
193f76ee892STomi Valkeinen {
194f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
195f76ee892STomi Valkeinen int r;
196f76ee892STomi Valkeinen u16 x1 = x;
197f76ee892STomi Valkeinen u16 x2 = x + w - 1;
198f76ee892STomi Valkeinen u16 y1 = y;
199f76ee892STomi Valkeinen u16 y2 = y + h - 1;
200f76ee892STomi Valkeinen
201f76ee892STomi Valkeinen u8 buf[5];
202f76ee892STomi Valkeinen buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
203f76ee892STomi Valkeinen buf[1] = (x1 >> 8) & 0xff;
204f76ee892STomi Valkeinen buf[2] = (x1 >> 0) & 0xff;
205f76ee892STomi Valkeinen buf[3] = (x2 >> 8) & 0xff;
206f76ee892STomi Valkeinen buf[4] = (x2 >> 0) & 0xff;
207f76ee892STomi Valkeinen
208f76ee892STomi Valkeinen r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
209f76ee892STomi Valkeinen if (r)
210f76ee892STomi Valkeinen return r;
211f76ee892STomi Valkeinen
212f76ee892STomi Valkeinen buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
213f76ee892STomi Valkeinen buf[1] = (y1 >> 8) & 0xff;
214f76ee892STomi Valkeinen buf[2] = (y1 >> 0) & 0xff;
215f76ee892STomi Valkeinen buf[3] = (y2 >> 8) & 0xff;
216f76ee892STomi Valkeinen buf[4] = (y2 >> 0) & 0xff;
217f76ee892STomi Valkeinen
218f76ee892STomi Valkeinen r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
219f76ee892STomi Valkeinen if (r)
220f76ee892STomi Valkeinen return r;
221f76ee892STomi Valkeinen
222f76ee892STomi Valkeinen in->ops.dsi->bta_sync(in, ddata->channel);
223f76ee892STomi Valkeinen
224f76ee892STomi Valkeinen return r;
225f76ee892STomi Valkeinen }
226f76ee892STomi Valkeinen
dsicm_queue_ulps_work(struct panel_drv_data * ddata)227f76ee892STomi Valkeinen static void dsicm_queue_ulps_work(struct panel_drv_data *ddata)
228f76ee892STomi Valkeinen {
229f76ee892STomi Valkeinen if (ddata->ulps_timeout > 0)
230b59ac9a3SBhaktipriya Shridhar schedule_delayed_work(&ddata->ulps_work,
231f76ee892STomi Valkeinen msecs_to_jiffies(ddata->ulps_timeout));
232f76ee892STomi Valkeinen }
233f76ee892STomi Valkeinen
dsicm_cancel_ulps_work(struct panel_drv_data * ddata)234f76ee892STomi Valkeinen static void dsicm_cancel_ulps_work(struct panel_drv_data *ddata)
235f76ee892STomi Valkeinen {
236f76ee892STomi Valkeinen cancel_delayed_work(&ddata->ulps_work);
237f76ee892STomi Valkeinen }
238f76ee892STomi Valkeinen
dsicm_enter_ulps(struct panel_drv_data * ddata)239f76ee892STomi Valkeinen static int dsicm_enter_ulps(struct panel_drv_data *ddata)
240f76ee892STomi Valkeinen {
241f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
242f76ee892STomi Valkeinen int r;
243f76ee892STomi Valkeinen
244f76ee892STomi Valkeinen if (ddata->ulps_enabled)
245f76ee892STomi Valkeinen return 0;
246f76ee892STomi Valkeinen
247f76ee892STomi Valkeinen dsicm_cancel_ulps_work(ddata);
248f76ee892STomi Valkeinen
249f76ee892STomi Valkeinen r = _dsicm_enable_te(ddata, false);
250f76ee892STomi Valkeinen if (r)
251f76ee892STomi Valkeinen goto err;
252f76ee892STomi Valkeinen
25367c366deSDmitry Torokhov if (ddata->ext_te_gpio)
25467c366deSDmitry Torokhov disable_irq(gpiod_to_irq(ddata->ext_te_gpio));
255f76ee892STomi Valkeinen
256f76ee892STomi Valkeinen in->ops.dsi->disable(in, false, true);
257f76ee892STomi Valkeinen
258f76ee892STomi Valkeinen ddata->ulps_enabled = true;
259f76ee892STomi Valkeinen
260f76ee892STomi Valkeinen return 0;
261f76ee892STomi Valkeinen
262f76ee892STomi Valkeinen err:
263f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "enter ULPS failed");
264f76ee892STomi Valkeinen dsicm_panel_reset(ddata);
265f76ee892STomi Valkeinen
266f76ee892STomi Valkeinen ddata->ulps_enabled = false;
267f76ee892STomi Valkeinen
268f76ee892STomi Valkeinen dsicm_queue_ulps_work(ddata);
269f76ee892STomi Valkeinen
270f76ee892STomi Valkeinen return r;
271f76ee892STomi Valkeinen }
272f76ee892STomi Valkeinen
dsicm_exit_ulps(struct panel_drv_data * ddata)273f76ee892STomi Valkeinen static int dsicm_exit_ulps(struct panel_drv_data *ddata)
274f76ee892STomi Valkeinen {
275f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
276f76ee892STomi Valkeinen int r;
277f76ee892STomi Valkeinen
278f76ee892STomi Valkeinen if (!ddata->ulps_enabled)
279f76ee892STomi Valkeinen return 0;
280f76ee892STomi Valkeinen
281f76ee892STomi Valkeinen r = in->ops.dsi->enable(in);
282f76ee892STomi Valkeinen if (r) {
283f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
284f76ee892STomi Valkeinen goto err1;
285f76ee892STomi Valkeinen }
286f76ee892STomi Valkeinen
287f76ee892STomi Valkeinen in->ops.dsi->enable_hs(in, ddata->channel, true);
288f76ee892STomi Valkeinen
289f76ee892STomi Valkeinen r = _dsicm_enable_te(ddata, true);
290f76ee892STomi Valkeinen if (r) {
291f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to re-enable TE");
292f76ee892STomi Valkeinen goto err2;
293f76ee892STomi Valkeinen }
294f76ee892STomi Valkeinen
29567c366deSDmitry Torokhov if (ddata->ext_te_gpio)
29667c366deSDmitry Torokhov enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
297f76ee892STomi Valkeinen
298f76ee892STomi Valkeinen dsicm_queue_ulps_work(ddata);
299f76ee892STomi Valkeinen
300f76ee892STomi Valkeinen ddata->ulps_enabled = false;
301f76ee892STomi Valkeinen
302f76ee892STomi Valkeinen return 0;
303f76ee892STomi Valkeinen
304f76ee892STomi Valkeinen err2:
305f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to exit ULPS");
306f76ee892STomi Valkeinen
307f76ee892STomi Valkeinen r = dsicm_panel_reset(ddata);
308f76ee892STomi Valkeinen if (!r) {
30967c366deSDmitry Torokhov if (ddata->ext_te_gpio)
31067c366deSDmitry Torokhov enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
311f76ee892STomi Valkeinen ddata->ulps_enabled = false;
312f76ee892STomi Valkeinen }
313f76ee892STomi Valkeinen err1:
314f76ee892STomi Valkeinen dsicm_queue_ulps_work(ddata);
315f76ee892STomi Valkeinen
316f76ee892STomi Valkeinen return r;
317f76ee892STomi Valkeinen }
318f76ee892STomi Valkeinen
dsicm_wake_up(struct panel_drv_data * ddata)319f76ee892STomi Valkeinen static int dsicm_wake_up(struct panel_drv_data *ddata)
320f76ee892STomi Valkeinen {
321f76ee892STomi Valkeinen if (ddata->ulps_enabled)
322f76ee892STomi Valkeinen return dsicm_exit_ulps(ddata);
323f76ee892STomi Valkeinen
324f76ee892STomi Valkeinen dsicm_cancel_ulps_work(ddata);
325f76ee892STomi Valkeinen dsicm_queue_ulps_work(ddata);
326f76ee892STomi Valkeinen return 0;
327f76ee892STomi Valkeinen }
328f76ee892STomi Valkeinen
dsicm_bl_update_status(struct backlight_device * dev)329f76ee892STomi Valkeinen static int dsicm_bl_update_status(struct backlight_device *dev)
330f76ee892STomi Valkeinen {
331f76ee892STomi Valkeinen struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
332f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
333f76ee892STomi Valkeinen int r;
334450afd92SStephen Kitt int level = backlight_get_brightness(dev);
335f76ee892STomi Valkeinen
336f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level);
337f76ee892STomi Valkeinen
338f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
339f76ee892STomi Valkeinen
340f76ee892STomi Valkeinen if (ddata->enabled) {
341f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
342f76ee892STomi Valkeinen
343f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
344f76ee892STomi Valkeinen if (!r)
345f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level);
346f76ee892STomi Valkeinen
347f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
348f76ee892STomi Valkeinen } else {
349f76ee892STomi Valkeinen r = 0;
350f76ee892STomi Valkeinen }
351f76ee892STomi Valkeinen
352f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
353f76ee892STomi Valkeinen
354f76ee892STomi Valkeinen return r;
355f76ee892STomi Valkeinen }
356f76ee892STomi Valkeinen
dsicm_bl_get_intensity(struct backlight_device * dev)357f76ee892STomi Valkeinen static int dsicm_bl_get_intensity(struct backlight_device *dev)
358f76ee892STomi Valkeinen {
359f76ee892STomi Valkeinen if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
360f76ee892STomi Valkeinen dev->props.power == FB_BLANK_UNBLANK)
361f76ee892STomi Valkeinen return dev->props.brightness;
362f76ee892STomi Valkeinen
363f76ee892STomi Valkeinen return 0;
364f76ee892STomi Valkeinen }
365f76ee892STomi Valkeinen
366f76ee892STomi Valkeinen static const struct backlight_ops dsicm_bl_ops = {
367f76ee892STomi Valkeinen .get_brightness = dsicm_bl_get_intensity,
368f76ee892STomi Valkeinen .update_status = dsicm_bl_update_status,
369f76ee892STomi Valkeinen };
370f76ee892STomi Valkeinen
dsicm_get_resolution(struct omap_dss_device * dssdev,u16 * xres,u16 * yres)371f76ee892STomi Valkeinen static void dsicm_get_resolution(struct omap_dss_device *dssdev,
372f76ee892STomi Valkeinen u16 *xres, u16 *yres)
373f76ee892STomi Valkeinen {
374f76ee892STomi Valkeinen *xres = dssdev->panel.timings.x_res;
375f76ee892STomi Valkeinen *yres = dssdev->panel.timings.y_res;
376f76ee892STomi Valkeinen }
377f76ee892STomi Valkeinen
dsicm_num_errors_show(struct device * dev,struct device_attribute * attr,char * buf)378f76ee892STomi Valkeinen static ssize_t dsicm_num_errors_show(struct device *dev,
379f76ee892STomi Valkeinen struct device_attribute *attr, char *buf)
380f76ee892STomi Valkeinen {
3816677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
382f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
383f76ee892STomi Valkeinen u8 errors = 0;
384f76ee892STomi Valkeinen int r;
385f76ee892STomi Valkeinen
386f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
387f76ee892STomi Valkeinen
388f76ee892STomi Valkeinen if (ddata->enabled) {
389f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
390f76ee892STomi Valkeinen
391f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
392f76ee892STomi Valkeinen if (!r)
393f76ee892STomi Valkeinen r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS,
394f76ee892STomi Valkeinen &errors);
395f76ee892STomi Valkeinen
396f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
397f76ee892STomi Valkeinen } else {
398f76ee892STomi Valkeinen r = -ENODEV;
399f76ee892STomi Valkeinen }
400f76ee892STomi Valkeinen
401f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
402f76ee892STomi Valkeinen
403f76ee892STomi Valkeinen if (r)
404f76ee892STomi Valkeinen return r;
405f76ee892STomi Valkeinen
406f63658a5SJing Yao return sysfs_emit(buf, "%d\n", errors);
407f76ee892STomi Valkeinen }
408f76ee892STomi Valkeinen
dsicm_hw_revision_show(struct device * dev,struct device_attribute * attr,char * buf)409f76ee892STomi Valkeinen static ssize_t dsicm_hw_revision_show(struct device *dev,
410f76ee892STomi Valkeinen struct device_attribute *attr, char *buf)
411f76ee892STomi Valkeinen {
4126677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
413f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
414f76ee892STomi Valkeinen u8 id1, id2, id3;
415f76ee892STomi Valkeinen int r;
416f76ee892STomi Valkeinen
417f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
418f76ee892STomi Valkeinen
419f76ee892STomi Valkeinen if (ddata->enabled) {
420f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
421f76ee892STomi Valkeinen
422f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
423f76ee892STomi Valkeinen if (!r)
424f76ee892STomi Valkeinen r = dsicm_get_id(ddata, &id1, &id2, &id3);
425f76ee892STomi Valkeinen
426f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
427f76ee892STomi Valkeinen } else {
428f76ee892STomi Valkeinen r = -ENODEV;
429f76ee892STomi Valkeinen }
430f76ee892STomi Valkeinen
431f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
432f76ee892STomi Valkeinen
433f76ee892STomi Valkeinen if (r)
434f76ee892STomi Valkeinen return r;
435f76ee892STomi Valkeinen
436f63658a5SJing Yao return sysfs_emit(buf, "%02x.%02x.%02x\n", id1, id2, id3);
437f76ee892STomi Valkeinen }
438f76ee892STomi Valkeinen
dsicm_store_ulps(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)439f76ee892STomi Valkeinen static ssize_t dsicm_store_ulps(struct device *dev,
440f76ee892STomi Valkeinen struct device_attribute *attr,
441f76ee892STomi Valkeinen const char *buf, size_t count)
442f76ee892STomi Valkeinen {
4436677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
444f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
445f76ee892STomi Valkeinen unsigned long t;
446f76ee892STomi Valkeinen int r;
447f76ee892STomi Valkeinen
448f76ee892STomi Valkeinen r = kstrtoul(buf, 0, &t);
449f76ee892STomi Valkeinen if (r)
450f76ee892STomi Valkeinen return r;
451f76ee892STomi Valkeinen
452f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
453f76ee892STomi Valkeinen
454f76ee892STomi Valkeinen if (ddata->enabled) {
455f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
456f76ee892STomi Valkeinen
457f76ee892STomi Valkeinen if (t)
458f76ee892STomi Valkeinen r = dsicm_enter_ulps(ddata);
459f76ee892STomi Valkeinen else
460f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
461f76ee892STomi Valkeinen
462f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
463f76ee892STomi Valkeinen }
464f76ee892STomi Valkeinen
465f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
466f76ee892STomi Valkeinen
467f76ee892STomi Valkeinen if (r)
468f76ee892STomi Valkeinen return r;
469f76ee892STomi Valkeinen
470f76ee892STomi Valkeinen return count;
471f76ee892STomi Valkeinen }
472f76ee892STomi Valkeinen
dsicm_show_ulps(struct device * dev,struct device_attribute * attr,char * buf)473f76ee892STomi Valkeinen static ssize_t dsicm_show_ulps(struct device *dev,
474f76ee892STomi Valkeinen struct device_attribute *attr,
475f76ee892STomi Valkeinen char *buf)
476f76ee892STomi Valkeinen {
4776677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
478f76ee892STomi Valkeinen unsigned t;
479f76ee892STomi Valkeinen
480f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
481f76ee892STomi Valkeinen t = ddata->ulps_enabled;
482f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
483f76ee892STomi Valkeinen
484f63658a5SJing Yao return sysfs_emit(buf, "%u\n", t);
485f76ee892STomi Valkeinen }
486f76ee892STomi Valkeinen
dsicm_store_ulps_timeout(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)487f76ee892STomi Valkeinen static ssize_t dsicm_store_ulps_timeout(struct device *dev,
488f76ee892STomi Valkeinen struct device_attribute *attr,
489f76ee892STomi Valkeinen const char *buf, size_t count)
490f76ee892STomi Valkeinen {
4916677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
492f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
493f76ee892STomi Valkeinen unsigned long t;
494f76ee892STomi Valkeinen int r;
495f76ee892STomi Valkeinen
496f76ee892STomi Valkeinen r = kstrtoul(buf, 0, &t);
497f76ee892STomi Valkeinen if (r)
498f76ee892STomi Valkeinen return r;
499f76ee892STomi Valkeinen
500f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
501f76ee892STomi Valkeinen ddata->ulps_timeout = t;
502f76ee892STomi Valkeinen
503f76ee892STomi Valkeinen if (ddata->enabled) {
504f76ee892STomi Valkeinen /* dsicm_wake_up will restart the timer */
505f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
506f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
507f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
508f76ee892STomi Valkeinen }
509f76ee892STomi Valkeinen
510f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
511f76ee892STomi Valkeinen
512f76ee892STomi Valkeinen if (r)
513f76ee892STomi Valkeinen return r;
514f76ee892STomi Valkeinen
515f76ee892STomi Valkeinen return count;
516f76ee892STomi Valkeinen }
517f76ee892STomi Valkeinen
dsicm_show_ulps_timeout(struct device * dev,struct device_attribute * attr,char * buf)518f76ee892STomi Valkeinen static ssize_t dsicm_show_ulps_timeout(struct device *dev,
519f76ee892STomi Valkeinen struct device_attribute *attr,
520f76ee892STomi Valkeinen char *buf)
521f76ee892STomi Valkeinen {
5226677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
523f76ee892STomi Valkeinen unsigned t;
524f76ee892STomi Valkeinen
525f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
526f76ee892STomi Valkeinen t = ddata->ulps_timeout;
527f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
528f76ee892STomi Valkeinen
529f63658a5SJing Yao return sysfs_emit(buf, "%u\n", t);
530f76ee892STomi Valkeinen }
531f76ee892STomi Valkeinen
532f76ee892STomi Valkeinen static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL);
533f76ee892STomi Valkeinen static DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL);
534f76ee892STomi Valkeinen static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
535f76ee892STomi Valkeinen dsicm_show_ulps, dsicm_store_ulps);
536f76ee892STomi Valkeinen static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
537f76ee892STomi Valkeinen dsicm_show_ulps_timeout, dsicm_store_ulps_timeout);
538f76ee892STomi Valkeinen
539f76ee892STomi Valkeinen static struct attribute *dsicm_attrs[] = {
540f76ee892STomi Valkeinen &dev_attr_num_dsi_errors.attr,
541f76ee892STomi Valkeinen &dev_attr_hw_revision.attr,
542f76ee892STomi Valkeinen &dev_attr_ulps.attr,
543f76ee892STomi Valkeinen &dev_attr_ulps_timeout.attr,
544f76ee892STomi Valkeinen NULL,
545f76ee892STomi Valkeinen };
546f76ee892STomi Valkeinen
5475ffe80c1SArvind Yadav static const struct attribute_group dsicm_attr_group = {
548f76ee892STomi Valkeinen .attrs = dsicm_attrs,
549f76ee892STomi Valkeinen };
550f76ee892STomi Valkeinen
dsicm_hw_reset(struct panel_drv_data * ddata)551f76ee892STomi Valkeinen static void dsicm_hw_reset(struct panel_drv_data *ddata)
552f76ee892STomi Valkeinen {
55367c366deSDmitry Torokhov /*
55467c366deSDmitry Torokhov * Note that we appear to activate the reset line here. However
55567c366deSDmitry Torokhov * existing DTSes specified incorrect polarity for it (active high),
55667c366deSDmitry Torokhov * so in fact this deasserts the reset line.
55767c366deSDmitry Torokhov */
55867c366deSDmitry Torokhov gpiod_set_value_cansleep(ddata->reset_gpio, 1);
559f76ee892STomi Valkeinen udelay(10);
560f76ee892STomi Valkeinen /* reset the panel */
56167c366deSDmitry Torokhov gpiod_set_value_cansleep(ddata->reset_gpio, 0);
56267c366deSDmitry Torokhov /* keep reset asserted */
563f76ee892STomi Valkeinen udelay(10);
56467c366deSDmitry Torokhov /* release reset line */
56567c366deSDmitry Torokhov gpiod_set_value_cansleep(ddata->reset_gpio, 1);
566f76ee892STomi Valkeinen /* wait after releasing reset */
567f76ee892STomi Valkeinen usleep_range(5000, 10000);
568f76ee892STomi Valkeinen }
569f76ee892STomi Valkeinen
dsicm_power_on(struct panel_drv_data * ddata)570f76ee892STomi Valkeinen static int dsicm_power_on(struct panel_drv_data *ddata)
571f76ee892STomi Valkeinen {
572f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
573f76ee892STomi Valkeinen u8 id1, id2, id3;
574f76ee892STomi Valkeinen int r;
575f76ee892STomi Valkeinen struct omap_dss_dsi_config dsi_config = {
576f76ee892STomi Valkeinen .mode = OMAP_DSS_DSI_CMD_MODE,
577f76ee892STomi Valkeinen .pixel_format = OMAP_DSS_DSI_FMT_RGB888,
578f76ee892STomi Valkeinen .timings = &ddata->timings,
579f76ee892STomi Valkeinen .hs_clk_min = 150000000,
580f76ee892STomi Valkeinen .hs_clk_max = 300000000,
581f76ee892STomi Valkeinen .lp_clk_min = 7000000,
582f76ee892STomi Valkeinen .lp_clk_max = 10000000,
583f76ee892STomi Valkeinen };
584f76ee892STomi Valkeinen
585f76ee892STomi Valkeinen if (ddata->pin_config.num_pins > 0) {
586f76ee892STomi Valkeinen r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
587f76ee892STomi Valkeinen if (r) {
588f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev,
589f76ee892STomi Valkeinen "failed to configure DSI pins\n");
590f76ee892STomi Valkeinen goto err0;
591f76ee892STomi Valkeinen }
592f76ee892STomi Valkeinen }
593f76ee892STomi Valkeinen
594f76ee892STomi Valkeinen r = in->ops.dsi->set_config(in, &dsi_config);
595f76ee892STomi Valkeinen if (r) {
596f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to configure DSI\n");
597f76ee892STomi Valkeinen goto err0;
598f76ee892STomi Valkeinen }
599f76ee892STomi Valkeinen
600f76ee892STomi Valkeinen r = in->ops.dsi->enable(in);
601f76ee892STomi Valkeinen if (r) {
602f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
603f76ee892STomi Valkeinen goto err0;
604f76ee892STomi Valkeinen }
605f76ee892STomi Valkeinen
606f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
607f76ee892STomi Valkeinen
608f76ee892STomi Valkeinen in->ops.dsi->enable_hs(in, ddata->channel, false);
609f76ee892STomi Valkeinen
610f76ee892STomi Valkeinen r = dsicm_sleep_out(ddata);
611f76ee892STomi Valkeinen if (r)
612f76ee892STomi Valkeinen goto err;
613f76ee892STomi Valkeinen
614f76ee892STomi Valkeinen r = dsicm_get_id(ddata, &id1, &id2, &id3);
615f76ee892STomi Valkeinen if (r)
616f76ee892STomi Valkeinen goto err;
617f76ee892STomi Valkeinen
618f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff);
619f76ee892STomi Valkeinen if (r)
620f76ee892STomi Valkeinen goto err;
621f76ee892STomi Valkeinen
622f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY,
623f76ee892STomi Valkeinen (1<<2) | (1<<5)); /* BL | BCTRL */
624f76ee892STomi Valkeinen if (r)
625f76ee892STomi Valkeinen goto err;
626f76ee892STomi Valkeinen
627f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT,
628f76ee892STomi Valkeinen MIPI_DCS_PIXEL_FMT_24BIT);
629f76ee892STomi Valkeinen if (r)
630f76ee892STomi Valkeinen goto err;
631f76ee892STomi Valkeinen
632f76ee892STomi Valkeinen r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON);
633f76ee892STomi Valkeinen if (r)
634f76ee892STomi Valkeinen goto err;
635f76ee892STomi Valkeinen
636f76ee892STomi Valkeinen r = _dsicm_enable_te(ddata, ddata->te_enabled);
637f76ee892STomi Valkeinen if (r)
638f76ee892STomi Valkeinen goto err;
639f76ee892STomi Valkeinen
640f76ee892STomi Valkeinen r = in->ops.dsi->enable_video_output(in, ddata->channel);
641f76ee892STomi Valkeinen if (r)
642f76ee892STomi Valkeinen goto err;
643f76ee892STomi Valkeinen
644f76ee892STomi Valkeinen ddata->enabled = 1;
645f76ee892STomi Valkeinen
646f76ee892STomi Valkeinen if (!ddata->intro_printed) {
647f76ee892STomi Valkeinen dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n",
648f76ee892STomi Valkeinen id1, id2, id3);
649f76ee892STomi Valkeinen ddata->intro_printed = true;
650f76ee892STomi Valkeinen }
651f76ee892STomi Valkeinen
652f76ee892STomi Valkeinen in->ops.dsi->enable_hs(in, ddata->channel, true);
653f76ee892STomi Valkeinen
654f76ee892STomi Valkeinen return 0;
655f76ee892STomi Valkeinen err:
656f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n");
657f76ee892STomi Valkeinen
658f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
659f76ee892STomi Valkeinen
660f76ee892STomi Valkeinen in->ops.dsi->disable(in, true, false);
661f76ee892STomi Valkeinen err0:
662f76ee892STomi Valkeinen return r;
663f76ee892STomi Valkeinen }
664f76ee892STomi Valkeinen
dsicm_power_off(struct panel_drv_data * ddata)665f76ee892STomi Valkeinen static void dsicm_power_off(struct panel_drv_data *ddata)
666f76ee892STomi Valkeinen {
667f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
668f76ee892STomi Valkeinen int r;
669f76ee892STomi Valkeinen
670f76ee892STomi Valkeinen in->ops.dsi->disable_video_output(in, ddata->channel);
671f76ee892STomi Valkeinen
672f76ee892STomi Valkeinen r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF);
673f76ee892STomi Valkeinen if (!r)
674f76ee892STomi Valkeinen r = dsicm_sleep_in(ddata);
675f76ee892STomi Valkeinen
676f76ee892STomi Valkeinen if (r) {
677f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev,
678f76ee892STomi Valkeinen "error disabling panel, issuing HW reset\n");
679f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
680f76ee892STomi Valkeinen }
681f76ee892STomi Valkeinen
682f76ee892STomi Valkeinen in->ops.dsi->disable(in, true, false);
683f76ee892STomi Valkeinen
684f76ee892STomi Valkeinen ddata->enabled = 0;
685f76ee892STomi Valkeinen }
686f76ee892STomi Valkeinen
dsicm_panel_reset(struct panel_drv_data * ddata)687f76ee892STomi Valkeinen static int dsicm_panel_reset(struct panel_drv_data *ddata)
688f76ee892STomi Valkeinen {
689f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "performing LCD reset\n");
690f76ee892STomi Valkeinen
691f76ee892STomi Valkeinen dsicm_power_off(ddata);
692f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
693f76ee892STomi Valkeinen return dsicm_power_on(ddata);
694f76ee892STomi Valkeinen }
695f76ee892STomi Valkeinen
dsicm_connect(struct omap_dss_device * dssdev)696f76ee892STomi Valkeinen static int dsicm_connect(struct omap_dss_device *dssdev)
697f76ee892STomi Valkeinen {
698f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
699f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
700f76ee892STomi Valkeinen struct device *dev = &ddata->pdev->dev;
701f76ee892STomi Valkeinen int r;
702f76ee892STomi Valkeinen
703f76ee892STomi Valkeinen if (omapdss_device_is_connected(dssdev))
704f76ee892STomi Valkeinen return 0;
705f76ee892STomi Valkeinen
706f76ee892STomi Valkeinen r = in->ops.dsi->connect(in, dssdev);
707f76ee892STomi Valkeinen if (r) {
708f76ee892STomi Valkeinen dev_err(dev, "Failed to connect to video source\n");
709f76ee892STomi Valkeinen return r;
710f76ee892STomi Valkeinen }
711f76ee892STomi Valkeinen
712f76ee892STomi Valkeinen r = in->ops.dsi->request_vc(ddata->in, &ddata->channel);
713f76ee892STomi Valkeinen if (r) {
714f76ee892STomi Valkeinen dev_err(dev, "failed to get virtual channel\n");
715f76ee892STomi Valkeinen goto err_req_vc;
716f76ee892STomi Valkeinen }
717f76ee892STomi Valkeinen
718f76ee892STomi Valkeinen r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH);
719f76ee892STomi Valkeinen if (r) {
720f76ee892STomi Valkeinen dev_err(dev, "failed to set VC_ID\n");
721f76ee892STomi Valkeinen goto err_vc_id;
722f76ee892STomi Valkeinen }
723f76ee892STomi Valkeinen
724f76ee892STomi Valkeinen return 0;
725f76ee892STomi Valkeinen
726f76ee892STomi Valkeinen err_vc_id:
727f76ee892STomi Valkeinen in->ops.dsi->release_vc(ddata->in, ddata->channel);
728f76ee892STomi Valkeinen err_req_vc:
729f76ee892STomi Valkeinen in->ops.dsi->disconnect(in, dssdev);
730f76ee892STomi Valkeinen return r;
731f76ee892STomi Valkeinen }
732f76ee892STomi Valkeinen
dsicm_disconnect(struct omap_dss_device * dssdev)733f76ee892STomi Valkeinen static void dsicm_disconnect(struct omap_dss_device *dssdev)
734f76ee892STomi Valkeinen {
735f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
736f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
737f76ee892STomi Valkeinen
738f76ee892STomi Valkeinen if (!omapdss_device_is_connected(dssdev))
739f76ee892STomi Valkeinen return;
740f76ee892STomi Valkeinen
741f76ee892STomi Valkeinen in->ops.dsi->release_vc(in, ddata->channel);
742f76ee892STomi Valkeinen in->ops.dsi->disconnect(in, dssdev);
743f76ee892STomi Valkeinen }
744f76ee892STomi Valkeinen
dsicm_enable(struct omap_dss_device * dssdev)745f76ee892STomi Valkeinen static int dsicm_enable(struct omap_dss_device *dssdev)
746f76ee892STomi Valkeinen {
747f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
748f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
749f76ee892STomi Valkeinen int r;
750f76ee892STomi Valkeinen
751f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "enable\n");
752f76ee892STomi Valkeinen
753f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
754f76ee892STomi Valkeinen
755f76ee892STomi Valkeinen if (!omapdss_device_is_connected(dssdev)) {
756f76ee892STomi Valkeinen r = -ENODEV;
757f76ee892STomi Valkeinen goto err;
758f76ee892STomi Valkeinen }
759f76ee892STomi Valkeinen
760f76ee892STomi Valkeinen if (omapdss_device_is_enabled(dssdev)) {
761f76ee892STomi Valkeinen r = 0;
762f76ee892STomi Valkeinen goto err;
763f76ee892STomi Valkeinen }
764f76ee892STomi Valkeinen
765f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
766f76ee892STomi Valkeinen
767f76ee892STomi Valkeinen r = dsicm_power_on(ddata);
768f76ee892STomi Valkeinen
769f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
770f76ee892STomi Valkeinen
771f76ee892STomi Valkeinen if (r)
772f76ee892STomi Valkeinen goto err;
773f76ee892STomi Valkeinen
774f76ee892STomi Valkeinen dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
775f76ee892STomi Valkeinen
776f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
777f76ee892STomi Valkeinen
778f76ee892STomi Valkeinen return 0;
779f76ee892STomi Valkeinen err:
780f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "enable failed\n");
781f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
782f76ee892STomi Valkeinen return r;
783f76ee892STomi Valkeinen }
784f76ee892STomi Valkeinen
dsicm_disable(struct omap_dss_device * dssdev)785f76ee892STomi Valkeinen static void dsicm_disable(struct omap_dss_device *dssdev)
786f76ee892STomi Valkeinen {
787f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
788f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
789f76ee892STomi Valkeinen int r;
790f76ee892STomi Valkeinen
791f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "disable\n");
792f76ee892STomi Valkeinen
793f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
794f76ee892STomi Valkeinen
795f76ee892STomi Valkeinen dsicm_cancel_ulps_work(ddata);
796f76ee892STomi Valkeinen
797f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
798f76ee892STomi Valkeinen
799f76ee892STomi Valkeinen if (omapdss_device_is_enabled(dssdev)) {
800f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
801f76ee892STomi Valkeinen if (!r)
802f76ee892STomi Valkeinen dsicm_power_off(ddata);
803f76ee892STomi Valkeinen }
804f76ee892STomi Valkeinen
805f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
806f76ee892STomi Valkeinen
807f76ee892STomi Valkeinen dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
808f76ee892STomi Valkeinen
809f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
810f76ee892STomi Valkeinen }
811f76ee892STomi Valkeinen
dsicm_framedone_cb(int err,void * data)812f76ee892STomi Valkeinen static void dsicm_framedone_cb(int err, void *data)
813f76ee892STomi Valkeinen {
814f76ee892STomi Valkeinen struct panel_drv_data *ddata = data;
815f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
816f76ee892STomi Valkeinen
817f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err);
818f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(ddata->in);
819f76ee892STomi Valkeinen }
820f76ee892STomi Valkeinen
dsicm_te_isr(int irq,void * data)821f76ee892STomi Valkeinen static irqreturn_t dsicm_te_isr(int irq, void *data)
822f76ee892STomi Valkeinen {
823f76ee892STomi Valkeinen struct panel_drv_data *ddata = data;
824f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
825f76ee892STomi Valkeinen int old;
826f76ee892STomi Valkeinen int r;
827f76ee892STomi Valkeinen
828f76ee892STomi Valkeinen old = atomic_cmpxchg(&ddata->do_update, 1, 0);
829f76ee892STomi Valkeinen
830f76ee892STomi Valkeinen if (old) {
831f76ee892STomi Valkeinen cancel_delayed_work(&ddata->te_timeout_work);
832f76ee892STomi Valkeinen
833f76ee892STomi Valkeinen r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
834f76ee892STomi Valkeinen ddata);
835f76ee892STomi Valkeinen if (r)
836f76ee892STomi Valkeinen goto err;
837f76ee892STomi Valkeinen }
838f76ee892STomi Valkeinen
839f76ee892STomi Valkeinen return IRQ_HANDLED;
840f76ee892STomi Valkeinen err:
841f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "start update failed\n");
842f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
843f76ee892STomi Valkeinen return IRQ_HANDLED;
844f76ee892STomi Valkeinen }
845f76ee892STomi Valkeinen
dsicm_te_timeout_work_callback(struct work_struct * work)846f76ee892STomi Valkeinen static void dsicm_te_timeout_work_callback(struct work_struct *work)
847f76ee892STomi Valkeinen {
848f76ee892STomi Valkeinen struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
849f76ee892STomi Valkeinen te_timeout_work.work);
850f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
851f76ee892STomi Valkeinen
852f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n");
853f76ee892STomi Valkeinen
854f76ee892STomi Valkeinen atomic_set(&ddata->do_update, 0);
855f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
856f76ee892STomi Valkeinen }
857f76ee892STomi Valkeinen
dsicm_update(struct omap_dss_device * dssdev,u16 x,u16 y,u16 w,u16 h)858f76ee892STomi Valkeinen static int dsicm_update(struct omap_dss_device *dssdev,
859f76ee892STomi Valkeinen u16 x, u16 y, u16 w, u16 h)
860f76ee892STomi Valkeinen {
861f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
862f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
863f76ee892STomi Valkeinen int r;
864f76ee892STomi Valkeinen
865f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
866f76ee892STomi Valkeinen
867f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
868f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
869f76ee892STomi Valkeinen
870f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
871f76ee892STomi Valkeinen if (r)
872f76ee892STomi Valkeinen goto err;
873f76ee892STomi Valkeinen
874f76ee892STomi Valkeinen if (!ddata->enabled) {
875f76ee892STomi Valkeinen r = 0;
876f76ee892STomi Valkeinen goto err;
877f76ee892STomi Valkeinen }
878f76ee892STomi Valkeinen
879f76ee892STomi Valkeinen /* XXX no need to send this every frame, but dsi break if not done */
880f76ee892STomi Valkeinen r = dsicm_set_update_window(ddata, 0, 0,
881f76ee892STomi Valkeinen dssdev->panel.timings.x_res,
882f76ee892STomi Valkeinen dssdev->panel.timings.y_res);
883f76ee892STomi Valkeinen if (r)
884f76ee892STomi Valkeinen goto err;
885f76ee892STomi Valkeinen
88667c366deSDmitry Torokhov if (ddata->te_enabled && ddata->ext_te_gpio) {
887f76ee892STomi Valkeinen schedule_delayed_work(&ddata->te_timeout_work,
888f76ee892STomi Valkeinen msecs_to_jiffies(250));
889f76ee892STomi Valkeinen atomic_set(&ddata->do_update, 1);
890f76ee892STomi Valkeinen } else {
891f76ee892STomi Valkeinen r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
892f76ee892STomi Valkeinen ddata);
893f76ee892STomi Valkeinen if (r)
894f76ee892STomi Valkeinen goto err;
895f76ee892STomi Valkeinen }
896f76ee892STomi Valkeinen
897f76ee892STomi Valkeinen /* note: no bus_unlock here. unlock is in framedone_cb */
898f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
899f76ee892STomi Valkeinen return 0;
900f76ee892STomi Valkeinen err:
901f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
902f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
903f76ee892STomi Valkeinen return r;
904f76ee892STomi Valkeinen }
905f76ee892STomi Valkeinen
dsicm_sync(struct omap_dss_device * dssdev)906f76ee892STomi Valkeinen static int dsicm_sync(struct omap_dss_device *dssdev)
907f76ee892STomi Valkeinen {
908f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
909f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
910f76ee892STomi Valkeinen
911f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "sync\n");
912f76ee892STomi Valkeinen
913f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
914f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
915f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
916f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
917f76ee892STomi Valkeinen
918f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "sync done\n");
919f76ee892STomi Valkeinen
920f76ee892STomi Valkeinen return 0;
921f76ee892STomi Valkeinen }
922f76ee892STomi Valkeinen
_dsicm_enable_te(struct panel_drv_data * ddata,bool enable)923f76ee892STomi Valkeinen static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
924f76ee892STomi Valkeinen {
925f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
926f76ee892STomi Valkeinen int r;
927f76ee892STomi Valkeinen
928f76ee892STomi Valkeinen if (enable)
929f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0);
930f76ee892STomi Valkeinen else
931f76ee892STomi Valkeinen r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF);
932f76ee892STomi Valkeinen
93367c366deSDmitry Torokhov if (!ddata->ext_te_gpio)
934f76ee892STomi Valkeinen in->ops.dsi->enable_te(in, enable);
935f76ee892STomi Valkeinen
936f76ee892STomi Valkeinen /* possible panel bug */
937f76ee892STomi Valkeinen msleep(100);
938f76ee892STomi Valkeinen
939f76ee892STomi Valkeinen return r;
940f76ee892STomi Valkeinen }
941f76ee892STomi Valkeinen
dsicm_enable_te(struct omap_dss_device * dssdev,bool enable)942f76ee892STomi Valkeinen static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable)
943f76ee892STomi Valkeinen {
944f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
945f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
946f76ee892STomi Valkeinen int r;
947f76ee892STomi Valkeinen
948f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
949f76ee892STomi Valkeinen
950f76ee892STomi Valkeinen if (ddata->te_enabled == enable)
951f76ee892STomi Valkeinen goto end;
952f76ee892STomi Valkeinen
953f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
954f76ee892STomi Valkeinen
955f76ee892STomi Valkeinen if (ddata->enabled) {
956f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
957f76ee892STomi Valkeinen if (r)
958f76ee892STomi Valkeinen goto err;
959f76ee892STomi Valkeinen
960f76ee892STomi Valkeinen r = _dsicm_enable_te(ddata, enable);
961f76ee892STomi Valkeinen if (r)
962f76ee892STomi Valkeinen goto err;
963f76ee892STomi Valkeinen }
964f76ee892STomi Valkeinen
965f76ee892STomi Valkeinen ddata->te_enabled = enable;
966f76ee892STomi Valkeinen
967f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
968f76ee892STomi Valkeinen end:
969f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
970f76ee892STomi Valkeinen
971f76ee892STomi Valkeinen return 0;
972f76ee892STomi Valkeinen err:
973f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
974f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
975f76ee892STomi Valkeinen
976f76ee892STomi Valkeinen return r;
977f76ee892STomi Valkeinen }
978f76ee892STomi Valkeinen
dsicm_get_te(struct omap_dss_device * dssdev)979f76ee892STomi Valkeinen static int dsicm_get_te(struct omap_dss_device *dssdev)
980f76ee892STomi Valkeinen {
981f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
982f76ee892STomi Valkeinen int r;
983f76ee892STomi Valkeinen
984f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
985f76ee892STomi Valkeinen r = ddata->te_enabled;
986f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
987f76ee892STomi Valkeinen
988f76ee892STomi Valkeinen return r;
989f76ee892STomi Valkeinen }
990f76ee892STomi Valkeinen
dsicm_memory_read(struct omap_dss_device * dssdev,void * buf,size_t size,u16 x,u16 y,u16 w,u16 h)991f76ee892STomi Valkeinen static int dsicm_memory_read(struct omap_dss_device *dssdev,
992f76ee892STomi Valkeinen void *buf, size_t size,
993f76ee892STomi Valkeinen u16 x, u16 y, u16 w, u16 h)
994f76ee892STomi Valkeinen {
995f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
996f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
997f76ee892STomi Valkeinen int r;
998f76ee892STomi Valkeinen int first = 1;
999f76ee892STomi Valkeinen int plen;
1000f76ee892STomi Valkeinen unsigned buf_used = 0;
1001f76ee892STomi Valkeinen
1002f76ee892STomi Valkeinen if (size < w * h * 3)
1003f76ee892STomi Valkeinen return -ENOMEM;
1004f76ee892STomi Valkeinen
1005f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
1006f76ee892STomi Valkeinen
1007f76ee892STomi Valkeinen if (!ddata->enabled) {
1008f76ee892STomi Valkeinen r = -ENODEV;
1009f76ee892STomi Valkeinen goto err1;
1010f76ee892STomi Valkeinen }
1011f76ee892STomi Valkeinen
1012f76ee892STomi Valkeinen size = min(w * h * 3,
1013f76ee892STomi Valkeinen dssdev->panel.timings.x_res *
1014f76ee892STomi Valkeinen dssdev->panel.timings.y_res * 3);
1015f76ee892STomi Valkeinen
1016f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
1017f76ee892STomi Valkeinen
1018f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
1019f76ee892STomi Valkeinen if (r)
1020f76ee892STomi Valkeinen goto err2;
1021f76ee892STomi Valkeinen
1022f76ee892STomi Valkeinen /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1023f76ee892STomi Valkeinen * use short packets. plen 32 works, but bigger packets seem to cause
1024f76ee892STomi Valkeinen * an error. */
1025f76ee892STomi Valkeinen if (size % 2)
1026f76ee892STomi Valkeinen plen = 1;
1027f76ee892STomi Valkeinen else
1028f76ee892STomi Valkeinen plen = 2;
1029f76ee892STomi Valkeinen
1030f76ee892STomi Valkeinen dsicm_set_update_window(ddata, x, y, w, h);
1031f76ee892STomi Valkeinen
1032f76ee892STomi Valkeinen r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen);
1033f76ee892STomi Valkeinen if (r)
1034f76ee892STomi Valkeinen goto err2;
1035f76ee892STomi Valkeinen
1036f76ee892STomi Valkeinen while (buf_used < size) {
1037f76ee892STomi Valkeinen u8 dcs_cmd = first ? 0x2e : 0x3e;
1038f76ee892STomi Valkeinen first = 0;
1039f76ee892STomi Valkeinen
1040f76ee892STomi Valkeinen r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd,
1041f76ee892STomi Valkeinen buf + buf_used, size - buf_used);
1042f76ee892STomi Valkeinen
1043f76ee892STomi Valkeinen if (r < 0) {
1044f76ee892STomi Valkeinen dev_err(dssdev->dev, "read error\n");
1045f76ee892STomi Valkeinen goto err3;
1046f76ee892STomi Valkeinen }
1047f76ee892STomi Valkeinen
1048f76ee892STomi Valkeinen buf_used += r;
1049f76ee892STomi Valkeinen
1050f76ee892STomi Valkeinen if (r < plen) {
1051f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "short read\n");
1052f76ee892STomi Valkeinen break;
1053f76ee892STomi Valkeinen }
1054f76ee892STomi Valkeinen
1055f76ee892STomi Valkeinen if (signal_pending(current)) {
1056f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "signal pending, "
1057f76ee892STomi Valkeinen "aborting memory read\n");
1058f76ee892STomi Valkeinen r = -ERESTARTSYS;
1059f76ee892STomi Valkeinen goto err3;
1060f76ee892STomi Valkeinen }
1061f76ee892STomi Valkeinen }
1062f76ee892STomi Valkeinen
1063f76ee892STomi Valkeinen r = buf_used;
1064f76ee892STomi Valkeinen
1065f76ee892STomi Valkeinen err3:
1066f76ee892STomi Valkeinen in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1);
1067f76ee892STomi Valkeinen err2:
1068f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
1069f76ee892STomi Valkeinen err1:
1070f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
1071f76ee892STomi Valkeinen return r;
1072f76ee892STomi Valkeinen }
1073f76ee892STomi Valkeinen
dsicm_ulps_work(struct work_struct * work)1074f76ee892STomi Valkeinen static void dsicm_ulps_work(struct work_struct *work)
1075f76ee892STomi Valkeinen {
1076f76ee892STomi Valkeinen struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
1077f76ee892STomi Valkeinen ulps_work.work);
1078f76ee892STomi Valkeinen struct omap_dss_device *dssdev = &ddata->dssdev;
1079f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
1080f76ee892STomi Valkeinen
1081f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
1082f76ee892STomi Valkeinen
1083f76ee892STomi Valkeinen if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) {
1084f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
1085f76ee892STomi Valkeinen return;
1086f76ee892STomi Valkeinen }
1087f76ee892STomi Valkeinen
1088f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
1089f76ee892STomi Valkeinen
1090f76ee892STomi Valkeinen dsicm_enter_ulps(ddata);
1091f76ee892STomi Valkeinen
1092f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
1093f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
1094f76ee892STomi Valkeinen }
1095f76ee892STomi Valkeinen
1096f76ee892STomi Valkeinen static struct omap_dss_driver dsicm_ops = {
1097f76ee892STomi Valkeinen .connect = dsicm_connect,
1098f76ee892STomi Valkeinen .disconnect = dsicm_disconnect,
1099f76ee892STomi Valkeinen
1100f76ee892STomi Valkeinen .enable = dsicm_enable,
1101f76ee892STomi Valkeinen .disable = dsicm_disable,
1102f76ee892STomi Valkeinen
1103f76ee892STomi Valkeinen .update = dsicm_update,
1104f76ee892STomi Valkeinen .sync = dsicm_sync,
1105f76ee892STomi Valkeinen
1106f76ee892STomi Valkeinen .get_resolution = dsicm_get_resolution,
1107f76ee892STomi Valkeinen .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1108f76ee892STomi Valkeinen
1109f76ee892STomi Valkeinen .enable_te = dsicm_enable_te,
1110f76ee892STomi Valkeinen .get_te = dsicm_get_te,
1111f76ee892STomi Valkeinen
1112f76ee892STomi Valkeinen .memory_read = dsicm_memory_read,
1113f76ee892STomi Valkeinen };
1114f76ee892STomi Valkeinen
dsicm_probe(struct platform_device * pdev)1115f76ee892STomi Valkeinen static int dsicm_probe(struct platform_device *pdev)
1116f76ee892STomi Valkeinen {
1117f76ee892STomi Valkeinen struct backlight_properties props;
1118f76ee892STomi Valkeinen struct panel_drv_data *ddata;
1119f76ee892STomi Valkeinen struct backlight_device *bldev = NULL;
1120f76ee892STomi Valkeinen struct device *dev = &pdev->dev;
1121f76ee892STomi Valkeinen struct omap_dss_device *dssdev;
1122f76ee892STomi Valkeinen int r;
1123f76ee892STomi Valkeinen
1124f76ee892STomi Valkeinen dev_dbg(dev, "probe\n");
1125f76ee892STomi Valkeinen
11260984097cSPeter Ujfalusi if (!pdev->dev.of_node)
11270984097cSPeter Ujfalusi return -ENODEV;
11280984097cSPeter Ujfalusi
1129f76ee892STomi Valkeinen ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
1130f76ee892STomi Valkeinen if (!ddata)
1131f76ee892STomi Valkeinen return -ENOMEM;
1132f76ee892STomi Valkeinen
1133f76ee892STomi Valkeinen platform_set_drvdata(pdev, ddata);
1134f76ee892STomi Valkeinen ddata->pdev = pdev;
1135f76ee892STomi Valkeinen
113667c366deSDmitry Torokhov ddata->in = omapdss_of_find_source_for_first_ep(pdev->dev.of_node);
113767c366deSDmitry Torokhov r = PTR_ERR_OR_ZERO(ddata->in);
113867c366deSDmitry Torokhov if (r) {
113967c366deSDmitry Torokhov dev_err(&pdev->dev, "failed to find video source: %d\n", r);
1140f76ee892STomi Valkeinen return r;
114167c366deSDmitry Torokhov }
1142f76ee892STomi Valkeinen
1143f76ee892STomi Valkeinen ddata->timings.x_res = 864;
1144f76ee892STomi Valkeinen ddata->timings.y_res = 480;
1145f76ee892STomi Valkeinen ddata->timings.pixelclock = 864 * 480 * 60;
1146f76ee892STomi Valkeinen
1147f76ee892STomi Valkeinen dssdev = &ddata->dssdev;
1148f76ee892STomi Valkeinen dssdev->dev = dev;
1149f76ee892STomi Valkeinen dssdev->driver = &dsicm_ops;
1150f76ee892STomi Valkeinen dssdev->panel.timings = ddata->timings;
1151f76ee892STomi Valkeinen dssdev->type = OMAP_DISPLAY_TYPE_DSI;
1152f76ee892STomi Valkeinen dssdev->owner = THIS_MODULE;
1153f76ee892STomi Valkeinen
1154f76ee892STomi Valkeinen dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
1155f76ee892STomi Valkeinen dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
1156f76ee892STomi Valkeinen OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
1157f76ee892STomi Valkeinen
1158f76ee892STomi Valkeinen r = omapdss_register_display(dssdev);
1159f76ee892STomi Valkeinen if (r) {
1160f76ee892STomi Valkeinen dev_err(dev, "Failed to register panel\n");
1161f76ee892STomi Valkeinen goto err_reg;
1162f76ee892STomi Valkeinen }
1163f76ee892STomi Valkeinen
1164f76ee892STomi Valkeinen mutex_init(&ddata->lock);
1165f76ee892STomi Valkeinen
1166f76ee892STomi Valkeinen atomic_set(&ddata->do_update, 0);
1167f76ee892STomi Valkeinen
116867c366deSDmitry Torokhov ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
116967c366deSDmitry Torokhov r = PTR_ERR_OR_ZERO(ddata->reset_gpio);
1170f76ee892STomi Valkeinen if (r) {
117167c366deSDmitry Torokhov dev_err(&pdev->dev, "Failed to request reset gpio: %d\n", r);
1172f76ee892STomi Valkeinen return r;
1173f76ee892STomi Valkeinen }
1174f76ee892STomi Valkeinen
117567c366deSDmitry Torokhov gpiod_set_consumer_name(ddata->reset_gpio, "taal rst");
117667c366deSDmitry Torokhov
117767c366deSDmitry Torokhov ddata->ext_te_gpio = devm_gpiod_get_optional(&pdev->dev, "te",
117867c366deSDmitry Torokhov GPIOD_IN);
117967c366deSDmitry Torokhov r = PTR_ERR_OR_ZERO(ddata->ext_te_gpio);
118067c366deSDmitry Torokhov if (r) {
118167c366deSDmitry Torokhov dev_err(&pdev->dev, "Failed to request TE gpio: %d\n", r);
118267c366deSDmitry Torokhov return r;
118367c366deSDmitry Torokhov }
118467c366deSDmitry Torokhov
118567c366deSDmitry Torokhov if (ddata->ext_te_gpio) {
118667c366deSDmitry Torokhov gpiod_set_consumer_name(ddata->ext_te_gpio, "taal irq");
118767c366deSDmitry Torokhov
118867c366deSDmitry Torokhov r = devm_request_irq(dev, gpiod_to_irq(ddata->ext_te_gpio),
1189f76ee892STomi Valkeinen dsicm_te_isr,
1190f76ee892STomi Valkeinen IRQF_TRIGGER_RISING,
1191f76ee892STomi Valkeinen "taal vsync", ddata);
1192f76ee892STomi Valkeinen
1193f76ee892STomi Valkeinen if (r) {
1194f76ee892STomi Valkeinen dev_err(dev, "IRQ request failed\n");
1195f76ee892STomi Valkeinen return r;
1196f76ee892STomi Valkeinen }
1197f76ee892STomi Valkeinen
1198f76ee892STomi Valkeinen INIT_DEFERRABLE_WORK(&ddata->te_timeout_work,
1199f76ee892STomi Valkeinen dsicm_te_timeout_work_callback);
1200f76ee892STomi Valkeinen
1201f76ee892STomi Valkeinen dev_dbg(dev, "Using GPIO TE\n");
1202f76ee892STomi Valkeinen }
1203f76ee892STomi Valkeinen
1204f76ee892STomi Valkeinen INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
1205f76ee892STomi Valkeinen
1206f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
1207f76ee892STomi Valkeinen
1208f76ee892STomi Valkeinen if (ddata->use_dsi_backlight) {
1209f76ee892STomi Valkeinen memset(&props, 0, sizeof(struct backlight_properties));
1210f76ee892STomi Valkeinen props.max_brightness = 255;
1211f76ee892STomi Valkeinen
1212f76ee892STomi Valkeinen props.type = BACKLIGHT_RAW;
1213f76ee892STomi Valkeinen bldev = backlight_device_register(dev_name(dev),
1214f76ee892STomi Valkeinen dev, ddata, &dsicm_bl_ops, &props);
1215f76ee892STomi Valkeinen if (IS_ERR(bldev)) {
1216f76ee892STomi Valkeinen r = PTR_ERR(bldev);
1217b59ac9a3SBhaktipriya Shridhar goto err_reg;
1218f76ee892STomi Valkeinen }
1219f76ee892STomi Valkeinen
1220f76ee892STomi Valkeinen ddata->bldev = bldev;
1221f76ee892STomi Valkeinen
1222f76ee892STomi Valkeinen bldev->props.fb_blank = FB_BLANK_UNBLANK;
1223f76ee892STomi Valkeinen bldev->props.power = FB_BLANK_UNBLANK;
1224f76ee892STomi Valkeinen bldev->props.brightness = 255;
1225f76ee892STomi Valkeinen
1226f76ee892STomi Valkeinen dsicm_bl_update_status(bldev);
1227f76ee892STomi Valkeinen }
1228f76ee892STomi Valkeinen
1229f76ee892STomi Valkeinen r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
1230f76ee892STomi Valkeinen if (r) {
1231f76ee892STomi Valkeinen dev_err(dev, "failed to create sysfs files\n");
1232f76ee892STomi Valkeinen goto err_sysfs_create;
1233f76ee892STomi Valkeinen }
1234f76ee892STomi Valkeinen
1235f76ee892STomi Valkeinen return 0;
1236f76ee892STomi Valkeinen
1237f76ee892STomi Valkeinen err_sysfs_create:
1238f76ee892STomi Valkeinen if (bldev != NULL)
1239f76ee892STomi Valkeinen backlight_device_unregister(bldev);
1240f76ee892STomi Valkeinen err_reg:
1241f76ee892STomi Valkeinen return r;
1242f76ee892STomi Valkeinen }
1243f76ee892STomi Valkeinen
dsicm_remove(struct platform_device * pdev)1244f76ee892STomi Valkeinen static int __exit dsicm_remove(struct platform_device *pdev)
1245f76ee892STomi Valkeinen {
1246f76ee892STomi Valkeinen struct panel_drv_data *ddata = platform_get_drvdata(pdev);
1247f76ee892STomi Valkeinen struct omap_dss_device *dssdev = &ddata->dssdev;
1248f76ee892STomi Valkeinen struct backlight_device *bldev;
1249f76ee892STomi Valkeinen
1250f76ee892STomi Valkeinen dev_dbg(&pdev->dev, "remove\n");
1251f76ee892STomi Valkeinen
1252f76ee892STomi Valkeinen omapdss_unregister_display(dssdev);
1253f76ee892STomi Valkeinen
1254f76ee892STomi Valkeinen dsicm_disable(dssdev);
1255f76ee892STomi Valkeinen dsicm_disconnect(dssdev);
1256f76ee892STomi Valkeinen
1257f76ee892STomi Valkeinen sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group);
1258f76ee892STomi Valkeinen
1259f76ee892STomi Valkeinen bldev = ddata->bldev;
1260f76ee892STomi Valkeinen if (bldev != NULL) {
1261f76ee892STomi Valkeinen bldev->props.power = FB_BLANK_POWERDOWN;
1262f76ee892STomi Valkeinen dsicm_bl_update_status(bldev);
1263f76ee892STomi Valkeinen backlight_device_unregister(bldev);
1264f76ee892STomi Valkeinen }
1265f76ee892STomi Valkeinen
1266f76ee892STomi Valkeinen omap_dss_put_device(ddata->in);
1267f76ee892STomi Valkeinen
1268f76ee892STomi Valkeinen dsicm_cancel_ulps_work(ddata);
1269f76ee892STomi Valkeinen
1270f76ee892STomi Valkeinen /* reset, to be sure that the panel is in a valid state */
1271f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
1272f76ee892STomi Valkeinen
1273f76ee892STomi Valkeinen return 0;
1274f76ee892STomi Valkeinen }
1275f76ee892STomi Valkeinen
1276f76ee892STomi Valkeinen static const struct of_device_id dsicm_of_match[] = {
1277f76ee892STomi Valkeinen { .compatible = "omapdss,panel-dsi-cm", },
1278f76ee892STomi Valkeinen {},
1279f76ee892STomi Valkeinen };
1280f76ee892STomi Valkeinen
1281f76ee892STomi Valkeinen MODULE_DEVICE_TABLE(of, dsicm_of_match);
1282f76ee892STomi Valkeinen
1283f76ee892STomi Valkeinen static struct platform_driver dsicm_driver = {
1284f76ee892STomi Valkeinen .probe = dsicm_probe,
1285f76ee892STomi Valkeinen .remove = __exit_p(dsicm_remove),
1286f76ee892STomi Valkeinen .driver = {
1287f76ee892STomi Valkeinen .name = "panel-dsi-cm",
1288f76ee892STomi Valkeinen .of_match_table = dsicm_of_match,
1289f76ee892STomi Valkeinen .suppress_bind_attrs = true,
1290f76ee892STomi Valkeinen },
1291f76ee892STomi Valkeinen };
1292f76ee892STomi Valkeinen
1293f76ee892STomi Valkeinen module_platform_driver(dsicm_driver);
1294f76ee892STomi Valkeinen
1295f76ee892STomi Valkeinen MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
1296f76ee892STomi Valkeinen MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");
1297f76ee892STomi Valkeinen MODULE_LICENSE("GPL");
1298