Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/video/fbdev/omap2/omapfb/displays/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 26 kB image not shown  

Quelle  panel-dsi-cm.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Generic DSI Command Mode panel driver
 *
 * Copyright (C) 2013 Texas Instruments
 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
 */


/* #define DEBUG */

#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/fb.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/workqueue.h>

#include <video/omapfb_dss.h>
#include <video/mipi_display.h>

/* DSI Virtual channel. Hardcoded for now. */
#define TCH 0

#define DCS_READ_NUM_ERRORS 0x05
#define DCS_BRIGHTNESS  0x51
#define DCS_CTRL_DISPLAY 0x53
#define DCS_GET_ID1  0xda
#define DCS_GET_ID2  0xdb
#define DCS_GET_ID3  0xdc

struct panel_drv_data {
 struct omap_dss_device dssdev;
 struct omap_dss_device *in;

 struct omap_video_timings timings;

 struct platform_device *pdev;

 struct mutex lock;

 struct backlight_device *bldev;

 unsigned long hw_guard_end; /* next value of jiffies when we can
 * issue the next sleep in/out command
 */

 unsigned long hw_guard_wait; /* max guard time in jiffies */

 /* panel HW configuration from DT or platform data */
 struct gpio_desc *reset_gpio;
 struct gpio_desc *ext_te_gpio;

 bool use_dsi_backlight;

 struct omap_dsi_pin_config pin_config;

 /* runtime variables */
 bool enabled;

 bool te_enabled;

 atomic_t do_update;
 int channel;

 struct delayed_work te_timeout_work;

 bool intro_printed;

 bool ulps_enabled;
 unsigned ulps_timeout;
 struct delayed_work ulps_work;
};

#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)

static irqreturn_t dsicm_te_isr(int irq, void *data);
static void dsicm_te_timeout_work_callback(struct work_struct *work);
static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable);

static int dsicm_panel_reset(struct panel_drv_data *ddata);

static void dsicm_ulps_work(struct work_struct *work);

static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
{
 ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
 ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
}

static void hw_guard_wait(struct panel_drv_data *ddata)
{
 unsigned long wait = ddata->hw_guard_end - jiffies;

 if ((long)wait > 0 && time_before_eq(wait, ddata->hw_guard_wait)) {
  set_current_state(TASK_UNINTERRUPTIBLE);
  schedule_timeout(wait);
 }
}

static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
{
 struct omap_dss_device *in = ddata->in;
 int r;
 u8 buf[1];

 r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, buf, 1);

 if (r < 0)
  return r;

 *data = buf[0];

 return 0;
}

static int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd)
{
 struct omap_dss_device *in = ddata->in;
 return in->ops.dsi->dcs_write(in, ddata->channel, &dcs_cmd, 1);
}

static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
{
 struct omap_dss_device *in = ddata->in;
 u8 buf[2] = { dcs_cmd, param };

 return in->ops.dsi->dcs_write(in, ddata->channel, buf, 2);
}

static int dsicm_sleep_in(struct panel_drv_data *ddata)

{
 struct omap_dss_device *in = ddata->in;
 u8 cmd;
 int r;

 hw_guard_wait(ddata);

 cmd = MIPI_DCS_ENTER_SLEEP_MODE;
 r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, &cmd, 1);
 if (r)
  return r;

 hw_guard_start(ddata, 120);

 usleep_range(5000, 10000);

 return 0;
}

static int dsicm_sleep_out(struct panel_drv_data *ddata)
{
 int r;

 hw_guard_wait(ddata);

 r = dsicm_dcs_write_0(ddata, MIPI_DCS_EXIT_SLEEP_MODE);
 if (r)
  return r;

 hw_guard_start(ddata, 120);

 usleep_range(5000, 10000);

 return 0;
}

static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)
{
 int r;

 r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1);
 if (r)
  return r;
 r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2);
 if (r)
  return r;
 r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3);
 if (r)
  return r;

 return 0;
}

static int dsicm_set_update_window(struct panel_drv_data *ddata,
  u16 x, u16 y, u16 w, u16 h)
{
 struct omap_dss_device *in = ddata->in;
 int r;
 u16 x1 = x;
 u16 x2 = x + w - 1;
 u16 y1 = y;
 u16 y2 = y + h - 1;

 u8 buf[5];
 buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
 buf[1] = (x1 >> 8) & 0xff;
 buf[2] = (x1 >> 0) & 0xff;
 buf[3] = (x2 >> 8) & 0xff;
 buf[4] = (x2 >> 0) & 0xff;

 r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
 if (r)
  return r;

 buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
 buf[1] = (y1 >> 8) & 0xff;
 buf[2] = (y1 >> 0) & 0xff;
 buf[3] = (y2 >> 8) & 0xff;
 buf[4] = (y2 >> 0) & 0xff;

 r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
 if (r)
  return r;

 in->ops.dsi->bta_sync(in, ddata->channel);

 return r;
}

static void dsicm_queue_ulps_work(struct panel_drv_data *ddata)
{
 if (ddata->ulps_timeout > 0)
  schedule_delayed_work(&ddata->ulps_work,
    msecs_to_jiffies(ddata->ulps_timeout));
}

static void dsicm_cancel_ulps_work(struct panel_drv_data *ddata)
{
 cancel_delayed_work(&ddata->ulps_work);
}

static int dsicm_enter_ulps(struct panel_drv_data *ddata)
{
 struct omap_dss_device *in = ddata->in;
 int r;

 if (ddata->ulps_enabled)
  return 0;

 dsicm_cancel_ulps_work(ddata);

 r = _dsicm_enable_te(ddata, false);
 if (r)
  goto err;

 if (ddata->ext_te_gpio)
  disable_irq(gpiod_to_irq(ddata->ext_te_gpio));

 in->ops.dsi->disable(in, falsetrue);

 ddata->ulps_enabled = true;

 return 0;

err:
 dev_err(&ddata->pdev->dev, "enter ULPS failed");
 dsicm_panel_reset(ddata);

 ddata->ulps_enabled = false;

 dsicm_queue_ulps_work(ddata);

 return r;
}

static int dsicm_exit_ulps(struct panel_drv_data *ddata)
{
 struct omap_dss_device *in = ddata->in;
 int r;

 if (!ddata->ulps_enabled)
  return 0;

 r = in->ops.dsi->enable(in);
 if (r) {
  dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
  goto err1;
 }

 in->ops.dsi->enable_hs(in, ddata->channel, true);

 r = _dsicm_enable_te(ddata, true);
 if (r) {
  dev_err(&ddata->pdev->dev, "failed to re-enable TE");
  goto err2;
 }

 if (ddata->ext_te_gpio)
  enable_irq(gpiod_to_irq(ddata->ext_te_gpio));

 dsicm_queue_ulps_work(ddata);

 ddata->ulps_enabled = false;

 return 0;

err2:
 dev_err(&ddata->pdev->dev, "failed to exit ULPS");

 r = dsicm_panel_reset(ddata);
 if (!r) {
  if (ddata->ext_te_gpio)
   enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
  ddata->ulps_enabled = false;
 }
err1:
 dsicm_queue_ulps_work(ddata);

 return r;
}

static int dsicm_wake_up(struct panel_drv_data *ddata)
{
 if (ddata->ulps_enabled)
  return dsicm_exit_ulps(ddata);

 dsicm_cancel_ulps_work(ddata);
 dsicm_queue_ulps_work(ddata);
 return 0;
}

static int dsicm_bl_update_status(struct backlight_device *dev)
{
 struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
 struct omap_dss_device *in = ddata->in;
 int r;
 int level = backlight_get_brightness(dev);

 dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level);

 mutex_lock(&ddata->lock);

 if (ddata->enabled) {
  in->ops.dsi->bus_lock(in);

  r = dsicm_wake_up(ddata);
  if (!r)
   r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level);

  in->ops.dsi->bus_unlock(in);
 } else {
  r = 0;
 }

 mutex_unlock(&ddata->lock);

 return r;
}

static int dsicm_bl_get_intensity(struct backlight_device *dev)
{
 return backlight_get_brightness(dev);
}

static const struct backlight_ops dsicm_bl_ops = {
 .get_brightness = dsicm_bl_get_intensity,
 .update_status  = dsicm_bl_update_status,
};

static void dsicm_get_resolution(struct omap_dss_device *dssdev,
  u16 *xres, u16 *yres)
{
 *xres = dssdev->panel.timings.x_res;
 *yres = dssdev->panel.timings.y_res;
}

static ssize_t dsicm_num_errors_show(struct device *dev,
  struct device_attribute *attr, char *buf)
{
 struct panel_drv_data *ddata = dev_get_drvdata(dev);
 struct omap_dss_device *in = ddata->in;
 u8 errors = 0;
 int r;

 mutex_lock(&ddata->lock);

 if (ddata->enabled) {
  in->ops.dsi->bus_lock(in);

  r = dsicm_wake_up(ddata);
  if (!r)
   r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS,
     &errors);

  in->ops.dsi->bus_unlock(in);
 } else {
  r = -ENODEV;
 }

 mutex_unlock(&ddata->lock);

 if (r)
  return r;

 return sysfs_emit(buf, "%d\n", errors);
}

static ssize_t dsicm_hw_revision_show(struct device *dev,
  struct device_attribute *attr, char *buf)
{
 struct panel_drv_data *ddata = dev_get_drvdata(dev);
 struct omap_dss_device *in = ddata->in;
 u8 id1, id2, id3;
 int r;

 mutex_lock(&ddata->lock);

 if (ddata->enabled) {
  in->ops.dsi->bus_lock(in);

  r = dsicm_wake_up(ddata);
  if (!r)
   r = dsicm_get_id(ddata, &id1, &id2, &id3);

  in->ops.dsi->bus_unlock(in);
 } else {
  r = -ENODEV;
 }

 mutex_unlock(&ddata->lock);

 if (r)
  return r;

 return sysfs_emit(buf, "%02x.%02x.%02x\n", id1, id2, id3);
}

static ssize_t dsicm_store_ulps(struct device *dev,
  struct device_attribute *attr,
  const char *buf, size_t count)
{
 struct panel_drv_data *ddata = dev_get_drvdata(dev);
 struct omap_dss_device *in = ddata->in;
 unsigned long t;
 int r;

 r = kstrtoul(buf, 0, &t);
 if (r)
  return r;

 mutex_lock(&ddata->lock);

 if (ddata->enabled) {
  in->ops.dsi->bus_lock(in);

  if (t)
   r = dsicm_enter_ulps(ddata);
  else
   r = dsicm_wake_up(ddata);

  in->ops.dsi->bus_unlock(in);
 }

 mutex_unlock(&ddata->lock);

 if (r)
  return r;

 return count;
}

static ssize_t dsicm_show_ulps(struct device *dev,
  struct device_attribute *attr,
  char *buf)
{
 struct panel_drv_data *ddata = dev_get_drvdata(dev);
 unsigned t;

 mutex_lock(&ddata->lock);
 t = ddata->ulps_enabled;
 mutex_unlock(&ddata->lock);

 return sysfs_emit(buf, "%u\n", t);
}

static ssize_t dsicm_store_ulps_timeout(struct device *dev,
  struct device_attribute *attr,
  const char *buf, size_t count)
{
 struct panel_drv_data *ddata = dev_get_drvdata(dev);
 struct omap_dss_device *in = ddata->in;
 unsigned long t;
 int r;

 r = kstrtoul(buf, 0, &t);
 if (r)
  return r;

 mutex_lock(&ddata->lock);
 ddata->ulps_timeout = t;

 if (ddata->enabled) {
  /* dsicm_wake_up will restart the timer */
  in->ops.dsi->bus_lock(in);
  r = dsicm_wake_up(ddata);
  in->ops.dsi->bus_unlock(in);
 }

 mutex_unlock(&ddata->lock);

 if (r)
  return r;

 return count;
}

static ssize_t dsicm_show_ulps_timeout(struct device *dev,
  struct device_attribute *attr,
  char *buf)
{
 struct panel_drv_data *ddata = dev_get_drvdata(dev);
 unsigned t;

 mutex_lock(&ddata->lock);
 t = ddata->ulps_timeout;
 mutex_unlock(&ddata->lock);

 return sysfs_emit(buf, "%u\n", t);
}

static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL);
static DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL);
static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
  dsicm_show_ulps, dsicm_store_ulps);
static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
  dsicm_show_ulps_timeout, dsicm_store_ulps_timeout);

static struct attribute *dsicm_attrs[] = {
 &dev_attr_num_dsi_errors.attr,
 &dev_attr_hw_revision.attr,
 &dev_attr_ulps.attr,
 &dev_attr_ulps_timeout.attr,
 NULL,
};

static const struct attribute_group dsicm_attr_group = {
 .attrs = dsicm_attrs,
};

static void dsicm_hw_reset(struct panel_drv_data *ddata)
{
 /*
 * Note that we appear to activate the reset line here. However
 * existing DTSes specified incorrect polarity for it (active high),
 * so in fact this deasserts the reset line.
 */

 gpiod_set_value_cansleep(ddata->reset_gpio, 1);
 udelay(10);
 /* reset the panel */
 gpiod_set_value_cansleep(ddata->reset_gpio, 0);
 /* keep reset asserted */
 udelay(10);
 /* release reset line */
 gpiod_set_value_cansleep(ddata->reset_gpio, 1);
 /* wait after releasing reset */
 usleep_range(5000, 10000);
}

static int dsicm_power_on(struct panel_drv_data *ddata)
{
 struct omap_dss_device *in = ddata->in;
 u8 id1, id2, id3;
 int r;
 struct omap_dss_dsi_config dsi_config = {
  .mode = OMAP_DSS_DSI_CMD_MODE,
  .pixel_format = OMAP_DSS_DSI_FMT_RGB888,
  .timings = &ddata->timings,
  .hs_clk_min = 150000000,
  .hs_clk_max = 300000000,
  .lp_clk_min = 7000000,
  .lp_clk_max = 10000000,
 };

 if (ddata->pin_config.num_pins > 0) {
  r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
  if (r) {
   dev_err(&ddata->pdev->dev,
    "failed to configure DSI pins\n");
   goto err0;
  }
 }

 r = in->ops.dsi->set_config(in, &dsi_config);
 if (r) {
  dev_err(&ddata->pdev->dev, "failed to configure DSI\n");
  goto err0;
 }

 r = in->ops.dsi->enable(in);
 if (r) {
  dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
  goto err0;
 }

 dsicm_hw_reset(ddata);

 in->ops.dsi->enable_hs(in, ddata->channel, false);

 r = dsicm_sleep_out(ddata);
 if (r)
  goto err;

 r = dsicm_get_id(ddata, &id1, &id2, &id3);
 if (r)
  goto err;

 r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff);
 if (r)
  goto err;

 r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY,
   (1<<2) | (1<<5)); /* BL | BCTRL */
 if (r)
  goto err;

 r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT,
  MIPI_DCS_PIXEL_FMT_24BIT);
 if (r)
  goto err;

 r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON);
 if (r)
  goto err;

 r = _dsicm_enable_te(ddata, ddata->te_enabled);
 if (r)
  goto err;

 r = in->ops.dsi->enable_video_output(in, ddata->channel);
 if (r)
  goto err;

 ddata->enabled = 1;

 if (!ddata->intro_printed) {
  dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n",
   id1, id2, id3);
  ddata->intro_printed = true;
 }

 in->ops.dsi->enable_hs(in, ddata->channel, true);

 return 0;
err:
 dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n");

 dsicm_hw_reset(ddata);

 in->ops.dsi->disable(in, truefalse);
err0:
 return r;
}

static void dsicm_power_off(struct panel_drv_data *ddata)
{
 struct omap_dss_device *in = ddata->in;
 int r;

 in->ops.dsi->disable_video_output(in, ddata->channel);

 r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF);
 if (!r)
  r = dsicm_sleep_in(ddata);

 if (r) {
  dev_err(&ddata->pdev->dev,
    "error disabling panel, issuing HW reset\n");
  dsicm_hw_reset(ddata);
 }

 in->ops.dsi->disable(in, truefalse);

 ddata->enabled = 0;
}

static int dsicm_panel_reset(struct panel_drv_data *ddata)
{
 dev_err(&ddata->pdev->dev, "performing LCD reset\n");

 dsicm_power_off(ddata);
 dsicm_hw_reset(ddata);
 return dsicm_power_on(ddata);
}

static int dsicm_connect(struct omap_dss_device *dssdev)
{
 struct panel_drv_data *ddata = to_panel_data(dssdev);
 struct omap_dss_device *in = ddata->in;
 struct device *dev = &ddata->pdev->dev;
 int r;

 if (omapdss_device_is_connected(dssdev))
  return 0;

 r = in->ops.dsi->connect(in, dssdev);
 if (r) {
  dev_err(dev, "Failed to connect to video source\n");
  return r;
 }

 r = in->ops.dsi->request_vc(ddata->in, &ddata->channel);
 if (r) {
  dev_err(dev, "failed to get virtual channel\n");
  goto err_req_vc;
 }

 r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH);
 if (r) {
  dev_err(dev, "failed to set VC_ID\n");
  goto err_vc_id;
 }

 return 0;

err_vc_id:
 in->ops.dsi->release_vc(ddata->in, ddata->channel);
err_req_vc:
 in->ops.dsi->disconnect(in, dssdev);
 return r;
}

static void dsicm_disconnect(struct omap_dss_device *dssdev)
{
 struct panel_drv_data *ddata = to_panel_data(dssdev);
 struct omap_dss_device *in = ddata->in;

 if (!omapdss_device_is_connected(dssdev))
  return;

 in->ops.dsi->release_vc(in, ddata->channel);
 in->ops.dsi->disconnect(in, dssdev);
}

static int dsicm_enable(struct omap_dss_device *dssdev)
{
 struct panel_drv_data *ddata = to_panel_data(dssdev);
 struct omap_dss_device *in = ddata->in;
 int r;

 dev_dbg(&ddata->pdev->dev, "enable\n");

 mutex_lock(&ddata->lock);

 if (!omapdss_device_is_connected(dssdev)) {
  r = -ENODEV;
  goto err;
 }

 if (omapdss_device_is_enabled(dssdev)) {
  r = 0;
  goto err;
 }

 in->ops.dsi->bus_lock(in);

 r = dsicm_power_on(ddata);

 in->ops.dsi->bus_unlock(in);

 if (r)
  goto err;

 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;

 mutex_unlock(&ddata->lock);

 return 0;
err:
 dev_dbg(&ddata->pdev->dev, "enable failed\n");
 mutex_unlock(&ddata->lock);
 return r;
}

static void dsicm_disable(struct omap_dss_device *dssdev)
{
 struct panel_drv_data *ddata = to_panel_data(dssdev);
 struct omap_dss_device *in = ddata->in;
 int r;

 dev_dbg(&ddata->pdev->dev, "disable\n");

 mutex_lock(&ddata->lock);

 dsicm_cancel_ulps_work(ddata);

 in->ops.dsi->bus_lock(in);

 if (omapdss_device_is_enabled(dssdev)) {
  r = dsicm_wake_up(ddata);
  if (!r)
   dsicm_power_off(ddata);
 }

 in->ops.dsi->bus_unlock(in);

 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;

 mutex_unlock(&ddata->lock);
}

static void dsicm_framedone_cb(int err, void *data)
{
 struct panel_drv_data *ddata = data;
 struct omap_dss_device *in = ddata->in;

 dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err);
 in->ops.dsi->bus_unlock(ddata->in);
}

static irqreturn_t dsicm_te_isr(int irq, void *data)
{
 struct panel_drv_data *ddata = data;
 struct omap_dss_device *in = ddata->in;
 int old;
 int r;

 old = atomic_cmpxchg(&ddata->do_update, 1, 0);

 if (old) {
  cancel_delayed_work(&ddata->te_timeout_work);

  r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
    ddata);
  if (r)
   goto err;
 }

 return IRQ_HANDLED;
err:
 dev_err(&ddata->pdev->dev, "start update failed\n");
 in->ops.dsi->bus_unlock(in);
 return IRQ_HANDLED;
}

static void dsicm_te_timeout_work_callback(struct work_struct *work)
{
 struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
     te_timeout_work.work);
 struct omap_dss_device *in = ddata->in;

 dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n");

 atomic_set(&ddata->do_update, 0);
 in->ops.dsi->bus_unlock(in);
}

static int dsicm_update(struct omap_dss_device *dssdev,
        u16 x, u16 y, u16 w, u16 h)
{
 struct panel_drv_data *ddata = to_panel_data(dssdev);
 struct omap_dss_device *in = ddata->in;
 int r;

 dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);

 mutex_lock(&ddata->lock);
 in->ops.dsi->bus_lock(in);

 r = dsicm_wake_up(ddata);
 if (r)
  goto err;

 if (!ddata->enabled) {
  r = 0;
  goto err;
 }

 /* XXX no need to send this every frame, but dsi break if not done */
 r = dsicm_set_update_window(ddata, 0, 0,
   dssdev->panel.timings.x_res,
   dssdev->panel.timings.y_res);
 if (r)
  goto err;

 if (ddata->te_enabled && ddata->ext_te_gpio) {
  schedule_delayed_work(&ddata->te_timeout_work,
    msecs_to_jiffies(250));
  atomic_set(&ddata->do_update, 1);
 } else {
  r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
    ddata);
  if (r)
   goto err;
 }

 /* note: no bus_unlock here. unlock is in framedone_cb */
 mutex_unlock(&ddata->lock);
 return 0;
err:
 in->ops.dsi->bus_unlock(in);
 mutex_unlock(&ddata->lock);
 return r;
}

static int dsicm_sync(struct omap_dss_device *dssdev)
{
 struct panel_drv_data *ddata = to_panel_data(dssdev);
 struct omap_dss_device *in = ddata->in;

 dev_dbg(&ddata->pdev->dev, "sync\n");

 mutex_lock(&ddata->lock);
 in->ops.dsi->bus_lock(in);
 in->ops.dsi->bus_unlock(in);
 mutex_unlock(&ddata->lock);

 dev_dbg(&ddata->pdev->dev, "sync done\n");

 return 0;
}

static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
{
 struct omap_dss_device *in = ddata->in;
 int r;

 if (enable)
  r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0);
 else
  r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF);

 if (!ddata->ext_te_gpio)
  in->ops.dsi->enable_te(in, enable);

 /* possible panel bug */
 msleep(100);

 return r;
}

static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable)
{
 struct panel_drv_data *ddata = to_panel_data(dssdev);
 struct omap_dss_device *in = ddata->in;
 int r;

 mutex_lock(&ddata->lock);

 if (ddata->te_enabled == enable)
  goto end;

 in->ops.dsi->bus_lock(in);

 if (ddata->enabled) {
  r = dsicm_wake_up(ddata);
  if (r)
   goto err;

  r = _dsicm_enable_te(ddata, enable);
  if (r)
   goto err;
 }

 ddata->te_enabled = enable;

 in->ops.dsi->bus_unlock(in);
end:
 mutex_unlock(&ddata->lock);

 return 0;
err:
 in->ops.dsi->bus_unlock(in);
 mutex_unlock(&ddata->lock);

 return r;
}

static int dsicm_get_te(struct omap_dss_device *dssdev)
{
 struct panel_drv_data *ddata = to_panel_data(dssdev);
 int r;

 mutex_lock(&ddata->lock);
 r = ddata->te_enabled;
 mutex_unlock(&ddata->lock);

 return r;
}

static int dsicm_memory_read(struct omap_dss_device *dssdev,
  void *buf, size_t size,
  u16 x, u16 y, u16 w, u16 h)
{
 struct panel_drv_data *ddata = to_panel_data(dssdev);
 struct omap_dss_device *in = ddata->in;
 int r;
 int first = 1;
 int plen;
 unsigned buf_used = 0;

 if (size < w * h * 3)
  return -ENOMEM;

 mutex_lock(&ddata->lock);

 if (!ddata->enabled) {
  r = -ENODEV;
  goto err1;
 }

 size = min(w * h * 3,
   dssdev->panel.timings.x_res *
   dssdev->panel.timings.y_res * 3);

 in->ops.dsi->bus_lock(in);

 r = dsicm_wake_up(ddata);
 if (r)
  goto err2;

 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
 * use short packets. plen 32 works, but bigger packets seem to cause
 * an error. */

 if (size % 2)
  plen = 1;
 else
  plen = 2;

 dsicm_set_update_window(ddata, x, y, w, h);

 r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen);
 if (r)
  goto err2;

 while (buf_used < size) {
  u8 dcs_cmd = first ? 0x2e : 0x3e;
  first = 0;

  r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd,
    buf + buf_used, size - buf_used);

  if (r < 0) {
   dev_err(dssdev->dev, "read error\n");
   goto err3;
  }

  buf_used += r;

  if (r < plen) {
   dev_err(&ddata->pdev->dev, "short read\n");
   break;
  }

  if (signal_pending(current)) {
   dev_err(&ddata->pdev->dev, "signal pending, "
     "aborting memory read\n");
   r = -ERESTARTSYS;
   goto err3;
  }
 }

 r = buf_used;

err3:
 in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1);
err2:
 in->ops.dsi->bus_unlock(in);
err1:
 mutex_unlock(&ddata->lock);
 return r;
}

static void dsicm_ulps_work(struct work_struct *work)
{
 struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
   ulps_work.work);
 struct omap_dss_device *dssdev = &ddata->dssdev;
 struct omap_dss_device *in = ddata->in;

 mutex_lock(&ddata->lock);

 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) {
  mutex_unlock(&ddata->lock);
  return;
 }

 in->ops.dsi->bus_lock(in);

 dsicm_enter_ulps(ddata);

 in->ops.dsi->bus_unlock(in);
 mutex_unlock(&ddata->lock);
}

static struct omap_dss_driver dsicm_ops = {
 .connect = dsicm_connect,
 .disconnect = dsicm_disconnect,

 .enable  = dsicm_enable,
 .disable = dsicm_disable,

 .update  = dsicm_update,
 .sync  = dsicm_sync,

 .get_resolution = dsicm_get_resolution,
 .get_recommended_bpp = omapdss_default_get_recommended_bpp,

 .enable_te = dsicm_enable_te,
 .get_te  = dsicm_get_te,

 .memory_read = dsicm_memory_read,
};

static int dsicm_probe(struct platform_device *pdev)
{
 struct backlight_properties props;
 struct panel_drv_data *ddata;
 struct backlight_device *bldev = NULL;
 struct device *dev = &pdev->dev;
 struct omap_dss_device *dssdev;
 int r;

 dev_dbg(dev, "probe\n");

 if (!pdev->dev.of_node)
  return -ENODEV;

 ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
 if (!ddata)
  return -ENOMEM;

 platform_set_drvdata(pdev, ddata);
 ddata->pdev = pdev;

 ddata->in = omapdss_of_find_source_for_first_ep(pdev->dev.of_node);
 r = PTR_ERR_OR_ZERO(ddata->in);
 if (r) {
  dev_err(&pdev->dev, "failed to find video source: %d\n", r);
  return r;
 }

 ddata->timings.x_res = 864;
 ddata->timings.y_res = 480;
 ddata->timings.pixelclock = 864 * 480 * 60;

 dssdev = &ddata->dssdev;
 dssdev->dev = dev;
 dssdev->driver = &dsicm_ops;
 dssdev->panel.timings = ddata->timings;
 dssdev->type = OMAP_DISPLAY_TYPE_DSI;
 dssdev->owner = THIS_MODULE;

 dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
 dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
  OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;

 r = omapdss_register_display(dssdev);
 if (r) {
  dev_err(dev, "Failed to register panel\n");
  goto err_reg;
 }

 mutex_init(&ddata->lock);

 atomic_set(&ddata->do_update, 0);

 ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
 r = PTR_ERR_OR_ZERO(ddata->reset_gpio);
 if (r) {
  dev_err(&pdev->dev, "Failed to request reset gpio: %d\n", r);
  return r;
 }

 gpiod_set_consumer_name(ddata->reset_gpio, "taal rst");

 ddata->ext_te_gpio = devm_gpiod_get_optional(&pdev->dev, "te",
           GPIOD_IN);
 r = PTR_ERR_OR_ZERO(ddata->ext_te_gpio);
 if (r) {
  dev_err(&pdev->dev, "Failed to request TE gpio: %d\n", r);
  return r;
 }

 if (ddata->ext_te_gpio) {
  gpiod_set_consumer_name(ddata->ext_te_gpio, "taal irq");

  r = devm_request_irq(dev, gpiod_to_irq(ddata->ext_te_gpio),
    dsicm_te_isr,
    IRQF_TRIGGER_RISING,
    "taal vsync", ddata);

  if (r) {
   dev_err(dev, "IRQ request failed\n");
   return r;
  }

  INIT_DEFERRABLE_WORK(&ddata->te_timeout_work,
     dsicm_te_timeout_work_callback);

  dev_dbg(dev, "Using GPIO TE\n");
 }

 INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);

 dsicm_hw_reset(ddata);

 if (ddata->use_dsi_backlight) {
  memset(&props, 0, sizeof(struct backlight_properties));
  props.max_brightness = 255;

  props.type = BACKLIGHT_RAW;
  bldev = backlight_device_register(dev_name(dev),
    dev, ddata, &dsicm_bl_ops, &props);
  if (IS_ERR(bldev)) {
   r = PTR_ERR(bldev);
   goto err_reg;
  }

  ddata->bldev = bldev;

  bldev->props.power = BACKLIGHT_POWER_ON;
  bldev->props.brightness = 255;

  dsicm_bl_update_status(bldev);
 }

 r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
 if (r) {
  dev_err(dev, "failed to create sysfs files\n");
  goto err_sysfs_create;
 }

 return 0;

err_sysfs_create:
 if (bldev != NULL)
  backlight_device_unregister(bldev);
err_reg:
 return r;
}

static void dsicm_remove(struct platform_device *pdev)
{
 struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 struct omap_dss_device *dssdev = &ddata->dssdev;
 struct backlight_device *bldev;

 dev_dbg(&pdev->dev, "remove\n");

 omapdss_unregister_display(dssdev);

 dsicm_disable(dssdev);
 dsicm_disconnect(dssdev);

 sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group);

 bldev = ddata->bldev;
 if (bldev != NULL) {
  bldev->props.power = BACKLIGHT_POWER_OFF;
  dsicm_bl_update_status(bldev);
  backlight_device_unregister(bldev);
 }

 omap_dss_put_device(ddata->in);

 dsicm_cancel_ulps_work(ddata);

 /* reset, to be sure that the panel is in a valid state */
 dsicm_hw_reset(ddata);
}

static const struct of_device_id dsicm_of_match[] = {
 { .compatible = "omapdss,panel-dsi-cm", },
 {},
};

MODULE_DEVICE_TABLE(of, dsicm_of_match);

static struct platform_driver dsicm_driver = {
 .probe = dsicm_probe,
 .remove = dsicm_remove,
 .driver = {
  .name = "panel-dsi-cm",
  .of_match_table = dsicm_of_match,
 },
};

module_platform_driver(dsicm_driver);

MODULE_AUTHOR("Tomi Valkeinen ");
MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");
MODULE_LICENSE("GPL");

Messung V0.5
C=96 H=93 G=94

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.