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