162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Sony ACX565AKM LCD Panel driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Original Driver Author: Imre Deak <imre.deak@nokia.com> 862306a36Sopenharmony_ci * Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com> 962306a36Sopenharmony_ci * Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/spi/spi.h> 1762306a36Sopenharmony_ci#include <linux/jiffies.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/backlight.h> 2062306a36Sopenharmony_ci#include <linux/fb.h> 2162306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2262306a36Sopenharmony_ci#include <linux/of.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <video/omapfb_dss.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define MIPID_CMD_READ_DISP_ID 0x04 2762306a36Sopenharmony_ci#define MIPID_CMD_READ_RED 0x06 2862306a36Sopenharmony_ci#define MIPID_CMD_READ_GREEN 0x07 2962306a36Sopenharmony_ci#define MIPID_CMD_READ_BLUE 0x08 3062306a36Sopenharmony_ci#define MIPID_CMD_READ_DISP_STATUS 0x09 3162306a36Sopenharmony_ci#define MIPID_CMD_RDDSDR 0x0F 3262306a36Sopenharmony_ci#define MIPID_CMD_SLEEP_IN 0x10 3362306a36Sopenharmony_ci#define MIPID_CMD_SLEEP_OUT 0x11 3462306a36Sopenharmony_ci#define MIPID_CMD_DISP_OFF 0x28 3562306a36Sopenharmony_ci#define MIPID_CMD_DISP_ON 0x29 3662306a36Sopenharmony_ci#define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51 3762306a36Sopenharmony_ci#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52 3862306a36Sopenharmony_ci#define MIPID_CMD_WRITE_CTRL_DISP 0x53 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5) 4162306a36Sopenharmony_ci#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4) 4262306a36Sopenharmony_ci#define CTRL_DISP_BACKLIGHT_ON (1 << 2) 4362306a36Sopenharmony_ci#define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define MIPID_CMD_READ_CTRL_DISP 0x54 4662306a36Sopenharmony_ci#define MIPID_CMD_WRITE_CABC 0x55 4762306a36Sopenharmony_ci#define MIPID_CMD_READ_CABC 0x56 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define MIPID_VER_LPH8923 3 5062306a36Sopenharmony_ci#define MIPID_VER_LS041Y3 4 5162306a36Sopenharmony_ci#define MIPID_VER_L4F00311 8 5262306a36Sopenharmony_ci#define MIPID_VER_ACX565AKM 9 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct panel_drv_data { 5562306a36Sopenharmony_ci struct omap_dss_device dssdev; 5662306a36Sopenharmony_ci struct omap_dss_device *in; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci int datapairs; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci struct omap_video_timings videomode; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci char *name; 6562306a36Sopenharmony_ci int enabled; 6662306a36Sopenharmony_ci int model; 6762306a36Sopenharmony_ci int revision; 6862306a36Sopenharmony_ci u8 display_id[3]; 6962306a36Sopenharmony_ci unsigned has_bc:1; 7062306a36Sopenharmony_ci unsigned has_cabc:1; 7162306a36Sopenharmony_ci unsigned cabc_mode; 7262306a36Sopenharmony_ci unsigned long hw_guard_end; /* next value of jiffies 7362306a36Sopenharmony_ci when we can issue the 7462306a36Sopenharmony_ci next sleep in/out command */ 7562306a36Sopenharmony_ci unsigned long hw_guard_wait; /* max guard time in jiffies */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci struct spi_device *spi; 7862306a36Sopenharmony_ci struct mutex mutex; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci struct backlight_device *bl_dev; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct omap_video_timings acx565akm_panel_timings = { 8462306a36Sopenharmony_ci .x_res = 800, 8562306a36Sopenharmony_ci .y_res = 480, 8662306a36Sopenharmony_ci .pixelclock = 24000000, 8762306a36Sopenharmony_ci .hfp = 28, 8862306a36Sopenharmony_ci .hsw = 4, 8962306a36Sopenharmony_ci .hbp = 24, 9062306a36Sopenharmony_ci .vfp = 3, 9162306a36Sopenharmony_ci .vsw = 3, 9262306a36Sopenharmony_ci .vbp = 4, 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, 9562306a36Sopenharmony_ci .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, 9862306a36Sopenharmony_ci .de_level = OMAPDSS_SIG_ACTIVE_HIGH, 9962306a36Sopenharmony_ci .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void acx565akm_transfer(struct panel_drv_data *ddata, int cmd, 10562306a36Sopenharmony_ci const u8 *wbuf, int wlen, u8 *rbuf, int rlen) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct spi_message m; 10862306a36Sopenharmony_ci struct spi_transfer *x, xfer[5]; 10962306a36Sopenharmony_ci int r; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci BUG_ON(ddata->spi == NULL); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci spi_message_init(&m); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci memset(xfer, 0, sizeof(xfer)); 11662306a36Sopenharmony_ci x = &xfer[0]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci cmd &= 0xff; 11962306a36Sopenharmony_ci x->tx_buf = &cmd; 12062306a36Sopenharmony_ci x->bits_per_word = 9; 12162306a36Sopenharmony_ci x->len = 2; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (rlen > 1 && wlen == 0) { 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * Between the command and the response data there is a 12662306a36Sopenharmony_ci * dummy clock cycle. Add an extra bit after the command 12762306a36Sopenharmony_ci * word to account for this. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci x->bits_per_word = 10; 13062306a36Sopenharmony_ci cmd <<= 1; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci spi_message_add_tail(x, &m); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (wlen) { 13562306a36Sopenharmony_ci x++; 13662306a36Sopenharmony_ci x->tx_buf = wbuf; 13762306a36Sopenharmony_ci x->len = wlen; 13862306a36Sopenharmony_ci x->bits_per_word = 9; 13962306a36Sopenharmony_ci spi_message_add_tail(x, &m); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (rlen) { 14362306a36Sopenharmony_ci x++; 14462306a36Sopenharmony_ci x->rx_buf = rbuf; 14562306a36Sopenharmony_ci x->len = rlen; 14662306a36Sopenharmony_ci spi_message_add_tail(x, &m); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci r = spi_sync(ddata->spi, &m); 15062306a36Sopenharmony_ci if (r < 0) 15162306a36Sopenharmony_ci dev_dbg(&ddata->spi->dev, "spi_sync %d\n", r); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic inline void acx565akm_cmd(struct panel_drv_data *ddata, int cmd) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci acx565akm_transfer(ddata, cmd, NULL, 0, NULL, 0); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic inline void acx565akm_write(struct panel_drv_data *ddata, 16062306a36Sopenharmony_ci int reg, const u8 *buf, int len) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci acx565akm_transfer(ddata, reg, buf, len, NULL, 0); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic inline void acx565akm_read(struct panel_drv_data *ddata, 16662306a36Sopenharmony_ci int reg, u8 *buf, int len) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci acx565akm_transfer(ddata, reg, NULL, 0, buf, len); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void hw_guard_start(struct panel_drv_data *ddata, int guard_msec) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci ddata->hw_guard_wait = msecs_to_jiffies(guard_msec); 17462306a36Sopenharmony_ci ddata->hw_guard_end = jiffies + ddata->hw_guard_wait; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void hw_guard_wait(struct panel_drv_data *ddata) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci unsigned long wait = ddata->hw_guard_end - jiffies; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if ((long)wait > 0 && wait <= ddata->hw_guard_wait) { 18262306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 18362306a36Sopenharmony_ci schedule_timeout(wait); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void set_sleep_mode(struct panel_drv_data *ddata, int on) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci int cmd; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (on) 19262306a36Sopenharmony_ci cmd = MIPID_CMD_SLEEP_IN; 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci cmd = MIPID_CMD_SLEEP_OUT; 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * We have to keep 120msec between sleep in/out commands. 19762306a36Sopenharmony_ci * (8.2.15, 8.2.16). 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci hw_guard_wait(ddata); 20062306a36Sopenharmony_ci acx565akm_cmd(ddata, cmd); 20162306a36Sopenharmony_ci hw_guard_start(ddata, 120); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void set_display_state(struct panel_drv_data *ddata, int enabled) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci acx565akm_cmd(ddata, cmd); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int panel_enabled(struct panel_drv_data *ddata) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci u32 disp_status; 21462306a36Sopenharmony_ci int enabled; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci acx565akm_read(ddata, MIPID_CMD_READ_DISP_STATUS, 21762306a36Sopenharmony_ci (u8 *)&disp_status, 4); 21862306a36Sopenharmony_ci disp_status = __be32_to_cpu(disp_status); 21962306a36Sopenharmony_ci enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); 22062306a36Sopenharmony_ci dev_dbg(&ddata->spi->dev, 22162306a36Sopenharmony_ci "LCD panel %senabled by bootloader (status 0x%04x)\n", 22262306a36Sopenharmony_ci enabled ? "" : "not ", disp_status); 22362306a36Sopenharmony_ci return enabled; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int panel_detect(struct panel_drv_data *ddata) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci acx565akm_read(ddata, MIPID_CMD_READ_DISP_ID, ddata->display_id, 3); 22962306a36Sopenharmony_ci dev_dbg(&ddata->spi->dev, "MIPI display ID: %02x%02x%02x\n", 23062306a36Sopenharmony_ci ddata->display_id[0], 23162306a36Sopenharmony_ci ddata->display_id[1], 23262306a36Sopenharmony_ci ddata->display_id[2]); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci switch (ddata->display_id[0]) { 23562306a36Sopenharmony_ci case 0x10: 23662306a36Sopenharmony_ci ddata->model = MIPID_VER_ACX565AKM; 23762306a36Sopenharmony_ci ddata->name = "acx565akm"; 23862306a36Sopenharmony_ci ddata->has_bc = 1; 23962306a36Sopenharmony_ci ddata->has_cabc = 1; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case 0x29: 24262306a36Sopenharmony_ci ddata->model = MIPID_VER_L4F00311; 24362306a36Sopenharmony_ci ddata->name = "l4f00311"; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci case 0x45: 24662306a36Sopenharmony_ci ddata->model = MIPID_VER_LPH8923; 24762306a36Sopenharmony_ci ddata->name = "lph8923"; 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci case 0x83: 25062306a36Sopenharmony_ci ddata->model = MIPID_VER_LS041Y3; 25162306a36Sopenharmony_ci ddata->name = "ls041y3"; 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci default: 25462306a36Sopenharmony_ci ddata->name = "unknown"; 25562306a36Sopenharmony_ci dev_err(&ddata->spi->dev, "invalid display ID\n"); 25662306a36Sopenharmony_ci return -ENODEV; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ddata->revision = ddata->display_id[1]; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci dev_info(&ddata->spi->dev, "omapfb: %s rev %02x LCD detected\n", 26262306a36Sopenharmony_ci ddata->name, ddata->revision); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/*----------------------Backlight Control-------------------------*/ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void enable_backlight_ctrl(struct panel_drv_data *ddata, int enable) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci u16 ctrl; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci acx565akm_read(ddata, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1); 27462306a36Sopenharmony_ci if (enable) { 27562306a36Sopenharmony_ci ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON | 27662306a36Sopenharmony_ci CTRL_DISP_BACKLIGHT_ON; 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON | 27962306a36Sopenharmony_ci CTRL_DISP_BACKLIGHT_ON); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci ctrl |= 1 << 8; 28362306a36Sopenharmony_ci acx565akm_write(ddata, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void set_cabc_mode(struct panel_drv_data *ddata, unsigned mode) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci u16 cabc_ctrl; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ddata->cabc_mode = mode; 29162306a36Sopenharmony_ci if (!ddata->enabled) 29262306a36Sopenharmony_ci return; 29362306a36Sopenharmony_ci cabc_ctrl = 0; 29462306a36Sopenharmony_ci acx565akm_read(ddata, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1); 29562306a36Sopenharmony_ci cabc_ctrl &= ~3; 29662306a36Sopenharmony_ci cabc_ctrl |= (1 << 8) | (mode & 3); 29762306a36Sopenharmony_ci acx565akm_write(ddata, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic unsigned get_cabc_mode(struct panel_drv_data *ddata) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci return ddata->cabc_mode; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic unsigned get_hw_cabc_mode(struct panel_drv_data *ddata) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci u8 cabc_ctrl; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci acx565akm_read(ddata, MIPID_CMD_READ_CABC, &cabc_ctrl, 1); 31062306a36Sopenharmony_ci return cabc_ctrl & 3; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic void acx565akm_set_brightness(struct panel_drv_data *ddata, int level) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci int bv; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci bv = level | (1 << 8); 31862306a36Sopenharmony_ci acx565akm_write(ddata, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (level) 32162306a36Sopenharmony_ci enable_backlight_ctrl(ddata, 1); 32262306a36Sopenharmony_ci else 32362306a36Sopenharmony_ci enable_backlight_ctrl(ddata, 0); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int acx565akm_get_actual_brightness(struct panel_drv_data *ddata) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci u8 bv; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci acx565akm_read(ddata, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return bv; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int acx565akm_bl_update_status(struct backlight_device *dev) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); 33962306a36Sopenharmony_ci int level; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci dev_dbg(&ddata->spi->dev, "%s\n", __func__); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (dev->props.fb_blank == FB_BLANK_UNBLANK && 34462306a36Sopenharmony_ci dev->props.power == FB_BLANK_UNBLANK) 34562306a36Sopenharmony_ci level = dev->props.brightness; 34662306a36Sopenharmony_ci else 34762306a36Sopenharmony_ci level = 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (ddata->has_bc) 35062306a36Sopenharmony_ci acx565akm_set_brightness(ddata, level); 35162306a36Sopenharmony_ci else 35262306a36Sopenharmony_ci return -ENODEV; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int acx565akm_bl_get_intensity(struct backlight_device *dev) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci dev_dbg(&dev->dev, "%s\n", __func__); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (!ddata->has_bc) 36462306a36Sopenharmony_ci return -ENODEV; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (dev->props.fb_blank == FB_BLANK_UNBLANK && 36762306a36Sopenharmony_ci dev->props.power == FB_BLANK_UNBLANK) { 36862306a36Sopenharmony_ci if (ddata->has_bc) 36962306a36Sopenharmony_ci return acx565akm_get_actual_brightness(ddata); 37062306a36Sopenharmony_ci else 37162306a36Sopenharmony_ci return dev->props.brightness; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int acx565akm_bl_update_status_locked(struct backlight_device *dev) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); 38062306a36Sopenharmony_ci int r; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci mutex_lock(&ddata->mutex); 38362306a36Sopenharmony_ci r = acx565akm_bl_update_status(dev); 38462306a36Sopenharmony_ci mutex_unlock(&ddata->mutex); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return r; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic int acx565akm_bl_get_intensity_locked(struct backlight_device *dev) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); 39262306a36Sopenharmony_ci int r; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci mutex_lock(&ddata->mutex); 39562306a36Sopenharmony_ci r = acx565akm_bl_get_intensity(dev); 39662306a36Sopenharmony_ci mutex_unlock(&ddata->mutex); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return r; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic const struct backlight_ops acx565akm_bl_ops = { 40262306a36Sopenharmony_ci .get_brightness = acx565akm_bl_get_intensity_locked, 40362306a36Sopenharmony_ci .update_status = acx565akm_bl_update_status_locked, 40462306a36Sopenharmony_ci}; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/*--------------------Auto Brightness control via Sysfs---------------------*/ 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic const char * const cabc_modes[] = { 40962306a36Sopenharmony_ci "off", /* always used when CABC is not supported */ 41062306a36Sopenharmony_ci "ui", 41162306a36Sopenharmony_ci "still-image", 41262306a36Sopenharmony_ci "moving-image", 41362306a36Sopenharmony_ci}; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic ssize_t show_cabc_mode(struct device *dev, 41662306a36Sopenharmony_ci struct device_attribute *attr, 41762306a36Sopenharmony_ci char *buf) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(dev); 42062306a36Sopenharmony_ci const char *mode_str; 42162306a36Sopenharmony_ci int mode; 42262306a36Sopenharmony_ci int len; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (!ddata->has_cabc) 42562306a36Sopenharmony_ci mode = 0; 42662306a36Sopenharmony_ci else 42762306a36Sopenharmony_ci mode = get_cabc_mode(ddata); 42862306a36Sopenharmony_ci mode_str = "unknown"; 42962306a36Sopenharmony_ci if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) 43062306a36Sopenharmony_ci mode_str = cabc_modes[mode]; 43162306a36Sopenharmony_ci len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic ssize_t store_cabc_mode(struct device *dev, 43762306a36Sopenharmony_ci struct device_attribute *attr, 43862306a36Sopenharmony_ci const char *buf, size_t count) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(dev); 44162306a36Sopenharmony_ci int i; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { 44462306a36Sopenharmony_ci const char *mode_str = cabc_modes[i]; 44562306a36Sopenharmony_ci int cmp_len = strlen(mode_str); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (count > 0 && buf[count - 1] == '\n') 44862306a36Sopenharmony_ci count--; 44962306a36Sopenharmony_ci if (count != cmp_len) 45062306a36Sopenharmony_ci continue; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (strncmp(buf, mode_str, cmp_len) == 0) 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (i == ARRAY_SIZE(cabc_modes)) 45762306a36Sopenharmony_ci return -EINVAL; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (!ddata->has_cabc && i != 0) 46062306a36Sopenharmony_ci return -EINVAL; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci mutex_lock(&ddata->mutex); 46362306a36Sopenharmony_ci set_cabc_mode(ddata, i); 46462306a36Sopenharmony_ci mutex_unlock(&ddata->mutex); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return count; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic ssize_t show_cabc_available_modes(struct device *dev, 47062306a36Sopenharmony_ci struct device_attribute *attr, 47162306a36Sopenharmony_ci char *buf) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(dev); 47462306a36Sopenharmony_ci int len; 47562306a36Sopenharmony_ci int i; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (!ddata->has_cabc) 47862306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", cabc_modes[0]); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci for (i = 0, len = 0; 48162306a36Sopenharmony_ci len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) 48262306a36Sopenharmony_ci len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", 48362306a36Sopenharmony_ci i ? " " : "", cabc_modes[i], 48462306a36Sopenharmony_ci i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return len < PAGE_SIZE ? len : PAGE_SIZE - 1; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, 49062306a36Sopenharmony_ci show_cabc_mode, store_cabc_mode); 49162306a36Sopenharmony_cistatic DEVICE_ATTR(cabc_available_modes, S_IRUGO, 49262306a36Sopenharmony_ci show_cabc_available_modes, NULL); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic struct attribute *bldev_attrs[] = { 49562306a36Sopenharmony_ci &dev_attr_cabc_mode.attr, 49662306a36Sopenharmony_ci &dev_attr_cabc_available_modes.attr, 49762306a36Sopenharmony_ci NULL, 49862306a36Sopenharmony_ci}; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic const struct attribute_group bldev_attr_group = { 50162306a36Sopenharmony_ci .attrs = bldev_attrs, 50262306a36Sopenharmony_ci}; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int acx565akm_connect(struct omap_dss_device *dssdev) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 50762306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (omapdss_device_is_connected(dssdev)) 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return in->ops.sdi->connect(in, dssdev); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void acx565akm_disconnect(struct omap_dss_device *dssdev) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 51862306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!omapdss_device_is_connected(dssdev)) 52162306a36Sopenharmony_ci return; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci in->ops.sdi->disconnect(in, dssdev); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int acx565akm_panel_power_on(struct omap_dss_device *dssdev) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 52962306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 53062306a36Sopenharmony_ci int r; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci dev_dbg(&ddata->spi->dev, "%s\n", __func__); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci in->ops.sdi->set_timings(in, &ddata->videomode); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (ddata->datapairs > 0) 53762306a36Sopenharmony_ci in->ops.sdi->set_datapairs(in, ddata->datapairs); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci r = in->ops.sdi->enable(in); 54062306a36Sopenharmony_ci if (r) { 54162306a36Sopenharmony_ci pr_err("%s sdi enable failed\n", __func__); 54262306a36Sopenharmony_ci return r; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /*FIXME tweak me */ 54662306a36Sopenharmony_ci msleep(50); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 54962306a36Sopenharmony_ci * Note that we appear to activate the reset line here. However 55062306a36Sopenharmony_ci * existing DTSes specified incorrect polarity for it (active high), 55162306a36Sopenharmony_ci * so in fact this deasserts the reset line. 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_ci if (ddata->reset_gpio) 55462306a36Sopenharmony_ci gpiod_set_value_cansleep(ddata->reset_gpio, 1); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (ddata->enabled) { 55762306a36Sopenharmony_ci dev_dbg(&ddata->spi->dev, "panel already enabled\n"); 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* 56262306a36Sopenharmony_ci * We have to meet all the following delay requirements: 56362306a36Sopenharmony_ci * 1. tRW: reset pulse width 10usec (7.12.1) 56462306a36Sopenharmony_ci * 2. tRT: reset cancel time 5msec (7.12.1) 56562306a36Sopenharmony_ci * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst 56662306a36Sopenharmony_ci * case (7.6.2) 56762306a36Sopenharmony_ci * 4. 120msec before the sleep out command (7.12.1) 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_ci msleep(120); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci set_sleep_mode(ddata, 0); 57262306a36Sopenharmony_ci ddata->enabled = 1; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* 5msec between sleep out and the next command. (8.2.16) */ 57562306a36Sopenharmony_ci usleep_range(5000, 10000); 57662306a36Sopenharmony_ci set_display_state(ddata, 1); 57762306a36Sopenharmony_ci set_cabc_mode(ddata, ddata->cabc_mode); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return acx565akm_bl_update_status(ddata->bl_dev); 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void acx565akm_panel_power_off(struct omap_dss_device *dssdev) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 58562306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci dev_dbg(dssdev->dev, "%s\n", __func__); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (!ddata->enabled) 59062306a36Sopenharmony_ci return; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci set_display_state(ddata, 0); 59362306a36Sopenharmony_ci set_sleep_mode(ddata, 1); 59462306a36Sopenharmony_ci ddata->enabled = 0; 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * We have to provide PCLK,HS,VS signals for 2 frames (worst case 59762306a36Sopenharmony_ci * ~50msec) after sending the sleep in command and asserting the 59862306a36Sopenharmony_ci * reset signal. We probably could assert the reset w/o the delay 59962306a36Sopenharmony_ci * but we still delay to avoid possible artifacts. (7.6.1) 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ci msleep(50); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* see comment in acx565akm_panel_power_on() */ 60462306a36Sopenharmony_ci if (ddata->reset_gpio) 60562306a36Sopenharmony_ci gpiod_set_value_cansleep(ddata->reset_gpio, 0); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* FIXME need to tweak this delay */ 60862306a36Sopenharmony_ci msleep(100); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci in->ops.sdi->disable(in); 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic int acx565akm_enable(struct omap_dss_device *dssdev) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 61662306a36Sopenharmony_ci int r; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci dev_dbg(dssdev->dev, "%s\n", __func__); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (!omapdss_device_is_connected(dssdev)) 62162306a36Sopenharmony_ci return -ENODEV; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (omapdss_device_is_enabled(dssdev)) 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci mutex_lock(&ddata->mutex); 62762306a36Sopenharmony_ci r = acx565akm_panel_power_on(dssdev); 62862306a36Sopenharmony_ci mutex_unlock(&ddata->mutex); 62962306a36Sopenharmony_ci if (r) 63062306a36Sopenharmony_ci return r; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic void acx565akm_disable(struct omap_dss_device *dssdev) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci dev_dbg(dssdev->dev, "%s\n", __func__); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (!omapdss_device_is_enabled(dssdev)) 64462306a36Sopenharmony_ci return; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci mutex_lock(&ddata->mutex); 64762306a36Sopenharmony_ci acx565akm_panel_power_off(dssdev); 64862306a36Sopenharmony_ci mutex_unlock(&ddata->mutex); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic void acx565akm_set_timings(struct omap_dss_device *dssdev, 65462306a36Sopenharmony_ci struct omap_video_timings *timings) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 65762306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci ddata->videomode = *timings; 66062306a36Sopenharmony_ci dssdev->panel.timings = *timings; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci in->ops.sdi->set_timings(in, timings); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic void acx565akm_get_timings(struct omap_dss_device *dssdev, 66662306a36Sopenharmony_ci struct omap_video_timings *timings) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci *timings = ddata->videomode; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int acx565akm_check_timings(struct omap_dss_device *dssdev, 67462306a36Sopenharmony_ci struct omap_video_timings *timings) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct panel_drv_data *ddata = to_panel_data(dssdev); 67762306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return in->ops.sdi->check_timings(in, timings); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic struct omap_dss_driver acx565akm_ops = { 68362306a36Sopenharmony_ci .connect = acx565akm_connect, 68462306a36Sopenharmony_ci .disconnect = acx565akm_disconnect, 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci .enable = acx565akm_enable, 68762306a36Sopenharmony_ci .disable = acx565akm_disable, 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci .set_timings = acx565akm_set_timings, 69062306a36Sopenharmony_ci .get_timings = acx565akm_get_timings, 69162306a36Sopenharmony_ci .check_timings = acx565akm_check_timings, 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci .get_resolution = omapdss_default_get_resolution, 69462306a36Sopenharmony_ci}; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic int acx565akm_probe(struct spi_device *spi) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct panel_drv_data *ddata; 69962306a36Sopenharmony_ci struct omap_dss_device *dssdev; 70062306a36Sopenharmony_ci struct backlight_device *bldev; 70162306a36Sopenharmony_ci int max_brightness, brightness; 70262306a36Sopenharmony_ci struct backlight_properties props; 70362306a36Sopenharmony_ci int r; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci dev_dbg(&spi->dev, "%s\n", __func__); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (!spi->dev.of_node) 70862306a36Sopenharmony_ci return -ENODEV; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci spi->mode = SPI_MODE_3; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); 71362306a36Sopenharmony_ci if (ddata == NULL) 71462306a36Sopenharmony_ci return -ENOMEM; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci dev_set_drvdata(&spi->dev, ddata); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ddata->spi = spi; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci mutex_init(&ddata->mutex); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci ddata->in = omapdss_of_find_source_for_first_ep(spi->dev.of_node); 72362306a36Sopenharmony_ci r = PTR_ERR_OR_ZERO(ddata->in); 72462306a36Sopenharmony_ci if (r) { 72562306a36Sopenharmony_ci dev_err(&spi->dev, "failed to find video source\n"); 72662306a36Sopenharmony_ci return r; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci ddata->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", 73062306a36Sopenharmony_ci GPIOD_OUT_LOW); 73162306a36Sopenharmony_ci r = PTR_ERR_OR_ZERO(ddata->reset_gpio); 73262306a36Sopenharmony_ci if (r) 73362306a36Sopenharmony_ci goto err_gpio; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (ddata->reset_gpio) { 73662306a36Sopenharmony_ci gpiod_set_consumer_name(ddata->reset_gpio, "lcd reset"); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* release the reset line */ 73962306a36Sopenharmony_ci gpiod_set_value_cansleep(ddata->reset_gpio, 1); 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* 74362306a36Sopenharmony_ci * After reset we have to wait 5 msec before the first 74462306a36Sopenharmony_ci * command can be sent. 74562306a36Sopenharmony_ci */ 74662306a36Sopenharmony_ci usleep_range(5000, 10000); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ddata->enabled = panel_enabled(ddata); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci r = panel_detect(ddata); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (!ddata->enabled && ddata->reset_gpio) 75362306a36Sopenharmony_ci gpiod_set_value_cansleep(ddata->reset_gpio, 0); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (r) { 75662306a36Sopenharmony_ci dev_err(&spi->dev, "%s panel detect error\n", __func__); 75762306a36Sopenharmony_ci goto err_detect; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci memset(&props, 0, sizeof(props)); 76162306a36Sopenharmony_ci props.fb_blank = FB_BLANK_UNBLANK; 76262306a36Sopenharmony_ci props.power = FB_BLANK_UNBLANK; 76362306a36Sopenharmony_ci props.type = BACKLIGHT_RAW; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci bldev = backlight_device_register("acx565akm", &ddata->spi->dev, 76662306a36Sopenharmony_ci ddata, &acx565akm_bl_ops, &props); 76762306a36Sopenharmony_ci if (IS_ERR(bldev)) { 76862306a36Sopenharmony_ci r = PTR_ERR(bldev); 76962306a36Sopenharmony_ci goto err_reg_bl; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci ddata->bl_dev = bldev; 77262306a36Sopenharmony_ci if (ddata->has_cabc) { 77362306a36Sopenharmony_ci r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group); 77462306a36Sopenharmony_ci if (r) { 77562306a36Sopenharmony_ci dev_err(&bldev->dev, 77662306a36Sopenharmony_ci "%s failed to create sysfs files\n", __func__); 77762306a36Sopenharmony_ci goto err_sysfs; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci ddata->cabc_mode = get_hw_cabc_mode(ddata); 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci max_brightness = 255; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (ddata->has_bc) 78562306a36Sopenharmony_ci brightness = acx565akm_get_actual_brightness(ddata); 78662306a36Sopenharmony_ci else 78762306a36Sopenharmony_ci brightness = 0; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci bldev->props.max_brightness = max_brightness; 79062306a36Sopenharmony_ci bldev->props.brightness = brightness; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci acx565akm_bl_update_status(bldev); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci ddata->videomode = acx565akm_panel_timings; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci dssdev = &ddata->dssdev; 79862306a36Sopenharmony_ci dssdev->dev = &spi->dev; 79962306a36Sopenharmony_ci dssdev->driver = &acx565akm_ops; 80062306a36Sopenharmony_ci dssdev->type = OMAP_DISPLAY_TYPE_SDI; 80162306a36Sopenharmony_ci dssdev->owner = THIS_MODULE; 80262306a36Sopenharmony_ci dssdev->panel.timings = ddata->videomode; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci r = omapdss_register_display(dssdev); 80562306a36Sopenharmony_ci if (r) { 80662306a36Sopenharmony_ci dev_err(&spi->dev, "Failed to register panel\n"); 80762306a36Sopenharmony_ci goto err_reg; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci return 0; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cierr_reg: 81362306a36Sopenharmony_ci sysfs_remove_group(&bldev->dev.kobj, &bldev_attr_group); 81462306a36Sopenharmony_cierr_sysfs: 81562306a36Sopenharmony_ci backlight_device_unregister(bldev); 81662306a36Sopenharmony_cierr_reg_bl: 81762306a36Sopenharmony_cierr_detect: 81862306a36Sopenharmony_cierr_gpio: 81962306a36Sopenharmony_ci omap_dss_put_device(ddata->in); 82062306a36Sopenharmony_ci return r; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic void acx565akm_remove(struct spi_device *spi) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); 82662306a36Sopenharmony_ci struct omap_dss_device *dssdev = &ddata->dssdev; 82762306a36Sopenharmony_ci struct omap_dss_device *in = ddata->in; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci dev_dbg(&ddata->spi->dev, "%s\n", __func__); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci sysfs_remove_group(&ddata->bl_dev->dev.kobj, &bldev_attr_group); 83262306a36Sopenharmony_ci backlight_device_unregister(ddata->bl_dev); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci omapdss_unregister_display(dssdev); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci acx565akm_disable(dssdev); 83762306a36Sopenharmony_ci acx565akm_disconnect(dssdev); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci omap_dss_put_device(in); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic const struct of_device_id acx565akm_of_match[] = { 84362306a36Sopenharmony_ci { .compatible = "omapdss,sony,acx565akm", }, 84462306a36Sopenharmony_ci {}, 84562306a36Sopenharmony_ci}; 84662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, acx565akm_of_match); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic struct spi_driver acx565akm_driver = { 84962306a36Sopenharmony_ci .driver = { 85062306a36Sopenharmony_ci .name = "acx565akm", 85162306a36Sopenharmony_ci .of_match_table = acx565akm_of_match, 85262306a36Sopenharmony_ci .suppress_bind_attrs = true, 85362306a36Sopenharmony_ci }, 85462306a36Sopenharmony_ci .probe = acx565akm_probe, 85562306a36Sopenharmony_ci .remove = acx565akm_remove, 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cimodule_spi_driver(acx565akm_driver); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ciMODULE_AUTHOR("Nokia Corporation"); 86162306a36Sopenharmony_ciMODULE_DESCRIPTION("acx565akm LCD Driver"); 86262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 863