Commit 66c6bf3a authored by Sebastian Reichel's avatar Sebastian Reichel Committed by Tomi Valkeinen

drm/omap: dsi: do ULPS in host driver

Move ULPS handling into the DSI host controller, so that we
no longer need a custom API for the DSI client.

Note that for now ULPS is always disabled.
Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
Acked-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-29-tomi.valkeinen@ti.com
parent 9f0eb51e
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
...@@ -65,22 +64,12 @@ struct panel_drv_data { ...@@ -65,22 +64,12 @@ struct panel_drv_data {
bool enabled; bool enabled;
bool intro_printed; bool intro_printed;
struct workqueue_struct *workqueue;
bool ulps_enabled;
unsigned int ulps_timeout;
struct delayed_work ulps_work;
}; };
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable); 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 dsicm_bl_power(struct panel_drv_data *ddata, bool enable) static void dsicm_bl_power(struct panel_drv_data *ddata, bool enable)
{ {
struct backlight_device *backlight; struct backlight_device *backlight;
...@@ -204,94 +193,6 @@ static int dsicm_set_update_window(struct panel_drv_data *ddata, ...@@ -204,94 +193,6 @@ static int dsicm_set_update_window(struct panel_drv_data *ddata,
return 0; return 0;
} }
static void dsicm_queue_ulps_work(struct panel_drv_data *ddata)
{
if (ddata->ulps_timeout > 0)
queue_delayed_work(ddata->workqueue, &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 *src = ddata->src;
int r;
if (ddata->ulps_enabled)
return 0;
dsicm_cancel_ulps_work(ddata);
r = _dsicm_enable_te(ddata, false);
if (r)
goto err;
src->ops->dsi.ulps(src, true);
ddata->ulps_enabled = true;
return 0;
err:
dev_err(&ddata->dsi->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 *src = ddata->src;
int r;
if (!ddata->ulps_enabled)
return 0;
src->ops->dsi.ulps(src, false);
ddata->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
r = _dsicm_enable_te(ddata, true);
if (r) {
dev_err(&ddata->dsi->dev, "failed to re-enable TE");
goto err2;
}
dsicm_queue_ulps_work(ddata);
ddata->ulps_enabled = false;
return 0;
err2:
dev_err(&ddata->dsi->dev, "failed to exit ULPS");
r = dsicm_panel_reset(ddata);
if (!r)
ddata->ulps_enabled = false;
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) static int dsicm_bl_update_status(struct backlight_device *dev)
{ {
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
...@@ -309,7 +210,6 @@ static int dsicm_bl_update_status(struct backlight_device *dev) ...@@ -309,7 +210,6 @@ static int dsicm_bl_update_status(struct backlight_device *dev)
mutex_lock(&ddata->lock); mutex_lock(&ddata->lock);
if (ddata->enabled) { if (ddata->enabled) {
r = dsicm_wake_up(ddata);
if (!r) if (!r)
r = dsicm_dcs_write_1( r = dsicm_dcs_write_1(
ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, level); ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, level);
...@@ -339,18 +239,12 @@ static ssize_t dsicm_num_errors_show(struct device *dev, ...@@ -339,18 +239,12 @@ static ssize_t dsicm_num_errors_show(struct device *dev,
{ {
struct panel_drv_data *ddata = dev_get_drvdata(dev); struct panel_drv_data *ddata = dev_get_drvdata(dev);
u8 errors = 0; u8 errors = 0;
int r; int r = -ENODEV;
mutex_lock(&ddata->lock); mutex_lock(&ddata->lock);
if (ddata->enabled) { if (ddata->enabled)
r = dsicm_wake_up(ddata); r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS, &errors);
if (!r)
r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS,
&errors);
} else {
r = -ENODEV;
}
mutex_unlock(&ddata->lock); mutex_unlock(&ddata->lock);
...@@ -365,17 +259,12 @@ static ssize_t dsicm_hw_revision_show(struct device *dev, ...@@ -365,17 +259,12 @@ static ssize_t dsicm_hw_revision_show(struct device *dev,
{ {
struct panel_drv_data *ddata = dev_get_drvdata(dev); struct panel_drv_data *ddata = dev_get_drvdata(dev);
u8 id1, id2, id3; u8 id1, id2, id3;
int r; int r = -ENODEV;
mutex_lock(&ddata->lock); mutex_lock(&ddata->lock);
if (ddata->enabled) { if (ddata->enabled)
r = dsicm_wake_up(ddata); r = dsicm_get_id(ddata, &id1, &id2, &id3);
if (!r)
r = dsicm_get_id(ddata, &id1, &id2, &id3);
} else {
r = -ENODEV;
}
mutex_unlock(&ddata->lock); mutex_unlock(&ddata->lock);
...@@ -385,103 +274,12 @@ static ssize_t dsicm_hw_revision_show(struct device *dev, ...@@ -385,103 +274,12 @@ static ssize_t dsicm_hw_revision_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); return snprintf(buf, PAGE_SIZE, "%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);
unsigned long t;
int r;
r = kstrtoul(buf, 0, &t);
if (r)
return r;
mutex_lock(&ddata->lock);
if (ddata->enabled) {
if (t)
r = dsicm_enter_ulps(ddata);
else
r = dsicm_wake_up(ddata);
}
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 int t;
mutex_lock(&ddata->lock);
t = ddata->ulps_enabled;
mutex_unlock(&ddata->lock);
return snprintf(buf, PAGE_SIZE, "%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);
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 */
r = dsicm_wake_up(ddata);
}
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 int t;
mutex_lock(&ddata->lock);
t = ddata->ulps_timeout;
mutex_unlock(&ddata->lock);
return snprintf(buf, PAGE_SIZE, "%u\n", t);
}
static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL); 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(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[] = { static struct attribute *dsicm_attrs[] = {
&dev_attr_num_dsi_errors.attr, &dev_attr_num_dsi_errors.attr,
&dev_attr_hw_revision.attr, &dev_attr_hw_revision.attr,
&dev_attr_ulps.attr,
&dev_attr_ulps_timeout.attr,
NULL, NULL,
}; };
...@@ -617,15 +415,6 @@ static void dsicm_power_off(struct panel_drv_data *ddata) ...@@ -617,15 +415,6 @@ static void dsicm_power_off(struct panel_drv_data *ddata)
ddata->enabled = false; ddata->enabled = false;
} }
static int dsicm_panel_reset(struct panel_drv_data *ddata)
{
dev_err(&ddata->dsi->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 *src, static int dsicm_connect(struct omap_dss_device *src,
struct omap_dss_device *dst) struct omap_dss_device *dst)
{ {
...@@ -667,17 +456,12 @@ static void dsicm_enable(struct omap_dss_device *dssdev) ...@@ -667,17 +456,12 @@ static void dsicm_enable(struct omap_dss_device *dssdev)
static void dsicm_disable(struct omap_dss_device *dssdev) static void dsicm_disable(struct omap_dss_device *dssdev)
{ {
struct panel_drv_data *ddata = to_panel_data(dssdev); struct panel_drv_data *ddata = to_panel_data(dssdev);
int r;
dsicm_bl_power(ddata, false); dsicm_bl_power(ddata, false);
mutex_lock(&ddata->lock); mutex_lock(&ddata->lock);
dsicm_cancel_ulps_work(ddata); dsicm_power_off(ddata);
r = dsicm_wake_up(ddata);
if (!r)
dsicm_power_off(ddata);
mutex_unlock(&ddata->lock); mutex_unlock(&ddata->lock);
} }
...@@ -700,10 +484,6 @@ static int dsicm_update(struct omap_dss_device *dssdev, ...@@ -700,10 +484,6 @@ static int dsicm_update(struct omap_dss_device *dssdev,
mutex_lock(&ddata->lock); mutex_lock(&ddata->lock);
r = dsicm_wake_up(ddata);
if (r)
goto err;
if (!ddata->enabled) { if (!ddata->enabled) {
r = 0; r = 0;
goto err; goto err;
...@@ -744,24 +524,6 @@ static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable) ...@@ -744,24 +524,6 @@ static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
return r; 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;
mutex_lock(&ddata->lock);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) {
mutex_unlock(&ddata->lock);
return;
}
dsicm_enter_ulps(ddata);
mutex_unlock(&ddata->lock);
}
static int dsicm_get_modes(struct omap_dss_device *dssdev, static int dsicm_get_modes(struct omap_dss_device *dssdev,
struct drm_connector *connector) struct drm_connector *connector)
{ {
...@@ -859,8 +621,6 @@ static int dsicm_probe_of(struct mipi_dsi_device *dsi) ...@@ -859,8 +621,6 @@ static int dsicm_probe_of(struct mipi_dsi_device *dsi)
else else
ddata->use_dsi_backlight = true; ddata->use_dsi_backlight = true;
/* TODO: ulps */
return 0; return 0;
} }
...@@ -907,13 +667,6 @@ static int dsicm_probe(struct mipi_dsi_device *dsi) ...@@ -907,13 +667,6 @@ static int dsicm_probe(struct mipi_dsi_device *dsi)
mutex_init(&ddata->lock); mutex_init(&ddata->lock);
ddata->workqueue = create_singlethread_workqueue("dsicm_wq");
if (!ddata->workqueue) {
r = -ENOMEM;
goto err_reg;
}
INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
dsicm_hw_reset(ddata); dsicm_hw_reset(ddata);
if (ddata->use_dsi_backlight) { if (ddata->use_dsi_backlight) {
...@@ -953,8 +706,6 @@ static int dsicm_probe(struct mipi_dsi_device *dsi) ...@@ -953,8 +706,6 @@ static int dsicm_probe(struct mipi_dsi_device *dsi)
err_dsi_attach: err_dsi_attach:
sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group); sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group);
err_bl: err_bl:
destroy_workqueue(ddata->workqueue);
err_reg:
if (ddata->extbldev) if (ddata->extbldev)
put_device(&ddata->extbldev->dev); put_device(&ddata->extbldev->dev);
...@@ -981,9 +732,6 @@ static int __exit dsicm_remove(struct mipi_dsi_device *dsi) ...@@ -981,9 +732,6 @@ static int __exit dsicm_remove(struct mipi_dsi_device *dsi)
if (ddata->extbldev) if (ddata->extbldev)
put_device(&ddata->extbldev->dev); put_device(&ddata->extbldev->dev);
dsicm_cancel_ulps_work(ddata);
destroy_workqueue(ddata->workqueue);
/* reset, to be sure that the panel is in a valid state */ /* reset, to be sure that the panel is in a valid state */
dsicm_hw_reset(ddata); dsicm_hw_reset(ddata);
......
...@@ -207,6 +207,8 @@ struct dsi_reg { u16 module; u16 idx; }; ...@@ -207,6 +207,8 @@ struct dsi_reg { u16 module; u16 idx; };
typedef void (*omap_dsi_isr_t) (void *arg, u32 mask); typedef void (*omap_dsi_isr_t) (void *arg, u32 mask);
struct dsi_data; struct dsi_data;
static void dsi_set_ulps_auto(struct dsi_data *dsi, bool enable);
static int dsi_display_init_dispc(struct dsi_data *dsi); static int dsi_display_init_dispc(struct dsi_data *dsi);
static void dsi_display_uninit_dispc(struct dsi_data *dsi); static void dsi_display_uninit_dispc(struct dsi_data *dsi);
...@@ -378,6 +380,9 @@ struct dsi_data { ...@@ -378,6 +380,9 @@ struct dsi_data {
bool te_enabled; bool te_enabled;
bool ulps_enabled; bool ulps_enabled;
bool ulps_auto_idle;
struct delayed_work ulps_work;
void (*framedone_callback)(int, void *); void (*framedone_callback)(int, void *);
void *framedone_data; void *framedone_data;
...@@ -3796,6 +3801,7 @@ static void dsi_handle_framedone(struct dsi_data *dsi, int error) ...@@ -3796,6 +3801,7 @@ static void dsi_handle_framedone(struct dsi_data *dsi, int error)
REG_FLD_MOD(dsi, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ REG_FLD_MOD(dsi, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
} }
dsi_set_ulps_auto(dsi, true);
dsi_bus_unlock(dsi); dsi_bus_unlock(dsi);
dsi->framedone_callback(error, dsi->framedone_data); dsi->framedone_callback(error, dsi->framedone_data);
...@@ -3853,6 +3859,7 @@ static int dsi_update(struct omap_dss_device *dssdev, int channel, ...@@ -3853,6 +3859,7 @@ static int dsi_update(struct omap_dss_device *dssdev, int channel,
struct dsi_data *dsi = to_dsi_data(dssdev); struct dsi_data *dsi = to_dsi_data(dssdev);
dsi_bus_lock(dsi); dsi_bus_lock(dsi);
dsi_set_ulps_auto(dsi, false);
dsi->update_channel = channel; dsi->update_channel = channel;
dsi->framedone_callback = callback; dsi->framedone_callback = callback;
...@@ -4121,22 +4128,6 @@ static void dsi_display_disable(struct omap_dss_device *dssdev) ...@@ -4121,22 +4128,6 @@ static void dsi_display_disable(struct omap_dss_device *dssdev)
dsi_bus_unlock(dsi); dsi_bus_unlock(dsi);
} }
static void dsi_ulps(struct omap_dss_device *dssdev, bool enable)
{
struct dsi_data *dsi = to_dsi_data(dssdev);
DSSDBG("dsi_ulps %d\n", enable);
dsi_bus_lock(dsi);
if (enable)
_dsi_display_disable(dsi, false, true);
else
_dsi_display_enable(dsi);
dsi_bus_unlock(dsi);
}
static int dsi_enable_te(struct dsi_data *dsi, bool enable) static int dsi_enable_te(struct dsi_data *dsi, bool enable)
{ {
dsi->te_enabled = enable; dsi->te_enabled = enable;
...@@ -4151,6 +4142,42 @@ static int dsi_enable_te(struct dsi_data *dsi, bool enable) ...@@ -4151,6 +4142,42 @@ static int dsi_enable_te(struct dsi_data *dsi, bool enable)
return 0; return 0;
} }
static void omap_dsi_ulps_work_callback(struct work_struct *work)
{
struct dsi_data *dsi = container_of(work, struct dsi_data,
ulps_work.work);
dsi_bus_lock(dsi);
dsi_enable_te(dsi, false);
_dsi_display_disable(dsi, false, true);
dsi_bus_unlock(dsi);
}
static void dsi_set_ulps_auto(struct dsi_data *dsi, bool enable)
{
WARN_ON(!dsi_bus_is_locked(dsi));
if (!dsi->ulps_auto_idle)
return;
if (enable) {
schedule_delayed_work(&dsi->ulps_work, msecs_to_jiffies(250));
} else {
cancel_delayed_work_sync(&dsi->ulps_work);
if (!dsi->ulps_enabled)
return;
dsi_bus_lock(dsi);
_dsi_display_enable(dsi);
dsi_enable_te(dsi, true);
dsi_bus_unlock(dsi);
}
}
#ifdef PRINT_VERBOSE_VM_TIMINGS #ifdef PRINT_VERBOSE_VM_TIMINGS
static void print_dsi_vm(const char *str, static void print_dsi_vm(const char *str,
const struct omap_dss_dsi_videomode_timings *t) const struct omap_dss_dsi_videomode_timings *t)
...@@ -4801,7 +4828,9 @@ static ssize_t omap_dsi_host_transfer(struct mipi_dsi_host *host, ...@@ -4801,7 +4828,9 @@ static ssize_t omap_dsi_host_transfer(struct mipi_dsi_host *host,
int r; int r;
dsi_bus_lock(dsi); dsi_bus_lock(dsi);
dsi_set_ulps_auto(dsi, false);
r = _omap_dsi_host_transfer(dsi, msg); r = _omap_dsi_host_transfer(dsi, msg);
dsi_set_ulps_auto(dsi, true);
dsi_bus_unlock(dsi); dsi_bus_unlock(dsi);
return r; return r;
...@@ -4841,8 +4870,6 @@ static const struct omap_dss_device_ops dsi_ops = { ...@@ -4841,8 +4870,6 @@ static const struct omap_dss_device_ops dsi_ops = {
.disable = dsi_display_disable, .disable = dsi_display_disable,
.dsi = { .dsi = {
.ulps = dsi_ulps,
.set_config = dsi_set_config, .set_config = dsi_set_config,
.enable_video_output = dsi_enable_video_output, .enable_video_output = dsi_enable_video_output,
...@@ -4971,6 +4998,14 @@ static int omap_dsi_host_attach(struct mipi_dsi_host *host, ...@@ -4971,6 +4998,14 @@ static int omap_dsi_host_attach(struct mipi_dsi_host *host,
dsi->vc[channel].dest = client; dsi->vc[channel].dest = client;
dsi->pix_fmt = client->format; dsi->pix_fmt = client->format;
INIT_DEFERRABLE_WORK(&dsi->ulps_work,
omap_dsi_ulps_work_callback);
dsi_bus_lock(dsi);
dsi->ulps_auto_idle = false;
dsi_set_ulps_auto(dsi, true);
dsi_bus_unlock(dsi);
return 0; return 0;
} }
......
...@@ -285,8 +285,6 @@ struct omap_dss_writeback_info { ...@@ -285,8 +285,6 @@ struct omap_dss_writeback_info {
struct omapdss_dsi_ops { struct omapdss_dsi_ops {
/* bus configuration */ /* bus configuration */
void (*ulps)(struct omap_dss_device *dssdev, bool enable);
int (*set_config)(struct omap_dss_device *dssdev, int (*set_config)(struct omap_dss_device *dssdev,
const struct omap_dss_dsi_config *cfg); const struct omap_dss_dsi_config *cfg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment