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