2019-06-03 07:44:50 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2009-10-28 11:59:56 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2009 Nokia Corporation
|
2018-05-24 14:46:19 +03:00
|
|
|
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
2009-10-28 11:59:56 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#define DSS_SUBSYS_NAME "DSI"
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
2017-08-05 01:43:53 +03:00
|
|
|
#include <linux/mfd/syscon.h>
|
|
|
|
#include <linux/regmap.h>
|
2009-10-28 11:59:56 +02:00
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/interrupt.h>
|
2020-12-15 12:45:57 +02:00
|
|
|
#include <linux/irq.h>
|
2009-10-28 11:59:56 +02:00
|
|
|
#include <linux/delay.h>
|
2020-12-15 12:45:57 +02:00
|
|
|
#include <linux/gpio/consumer.h>
|
2009-10-28 11:59:56 +02:00
|
|
|
#include <linux/mutex.h>
|
2011-07-03 16:17:28 -04:00
|
|
|
#include <linux/module.h>
|
2010-01-11 16:33:56 +02:00
|
|
|
#include <linux/semaphore.h>
|
2009-10-28 11:59:56 +02:00
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
#include <linux/wait.h>
|
2010-01-12 14:16:41 +02:00
|
|
|
#include <linux/workqueue.h>
|
2010-07-28 15:53:38 +03:00
|
|
|
#include <linux/sched.h>
|
2011-05-12 17:26:27 +05:30
|
|
|
#include <linux/slab.h>
|
2011-05-12 17:26:29 +05:30
|
|
|
#include <linux/debugfs.h>
|
2011-05-27 10:52:19 +03:00
|
|
|
#include <linux/pm_runtime.h>
|
2012-08-21 15:35:42 +03:00
|
|
|
#include <linux/of.h>
|
2017-03-22 08:26:08 -05:00
|
|
|
#include <linux/of_graph.h>
|
2012-08-21 15:35:42 +03:00
|
|
|
#include <linux/of_platform.h>
|
OMAPDSS: componentize omapdss
omapdss kernel module contains drivers for multiple devices, one for
each DSS submodule. The probing we have at the moment is a mess, and
doesn't give us proper deferred probing nor ensure that all the devices
are probed before omapfb/omapdrm start using omapdss.
This patch solves the mess by using the component system for DSS
submodules.
The changes to all DSS submodules (dispc, dpi, dsi, hdmi4/5, rfbi, sdi,
venc) are the same: probe & remove functions are changed to bind &
unbind, and new probe & remove functions are added which call
component_add/del.
The dss_core driver (dss.c) acts as a component master. Adding and
matching the components is simple: all dss device's child devices are
added as components.
However, we do have some dependencies between the drivers. The order in
which they should be probed is reflected by the list in core.c
(dss_output_drv_reg_funcs). The drivers are registered in that order,
which causes the components to be added in that order, which makes the
components to be bound in that order. This feels a bit fragile, and we
probably should improve the code to manage binds in random order.
However, for now, this works fine.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2015-06-04 15:22:23 +03:00
|
|
|
#include <linux/component.h>
|
2017-08-05 01:44:10 +03:00
|
|
|
#include <linux/sys_soc.h>
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:13 +02:00
|
|
|
#include <drm/drm_bridge.h>
|
2020-12-15 12:45:36 +02:00
|
|
|
#include <drm/drm_mipi_dsi.h>
|
2020-12-15 12:46:05 +02:00
|
|
|
#include <drm/drm_panel.h>
|
2011-08-25 18:25:03 +05:30
|
|
|
#include <video/mipi_display.h>
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2016-05-27 14:40:49 +03:00
|
|
|
#include "omapdss.h"
|
2009-10-28 11:59:56 +02:00
|
|
|
#include "dss.h"
|
|
|
|
|
|
|
|
#define DSI_CATCH_MISSING_TE
|
|
|
|
|
2020-12-15 12:46:47 +02:00
|
|
|
#include "dsi.h"
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
#define REG_GET(dsi, idx, start, end) \
|
|
|
|
FLD_GET(dsi_read_reg(dsi, idx), start, end)
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
#define REG_FLD_MOD(dsi, idx, val, start, end) \
|
|
|
|
dsi_write_reg(dsi, idx, FLD_MOD(dsi_read_reg(dsi, idx), val, start, end))
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:51 +02:00
|
|
|
static int dsi_init_dispc(struct dsi_data *dsi);
|
|
|
|
static void dsi_uninit_dispc(struct dsi_data *dsi);
|
2013-02-22 12:58:35 +02:00
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel);
|
2013-05-15 11:24:30 +03:00
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static ssize_t _omap_dsi_host_transfer(struct dsi_data *dsi, int vc,
|
2020-12-15 12:46:02 +02:00
|
|
|
const struct mipi_dsi_msg *msg);
|
|
|
|
|
2013-10-02 14:41:24 +03:00
|
|
|
#ifdef DSI_PERF_MEASURE
|
2012-01-13 09:32:20 +10:30
|
|
|
static bool dsi_perf;
|
|
|
|
module_param(dsi_perf, bool, 0644);
|
2009-10-28 11:59:56 +02:00
|
|
|
#endif
|
|
|
|
|
2020-12-15 12:46:43 +02:00
|
|
|
/* Note: for some reason video mode seems to work only if VC_VIDEO is 0 */
|
|
|
|
#define VC_VIDEO 0
|
|
|
|
#define VC_CMD 1
|
2020-12-15 12:46:35 +02:00
|
|
|
|
2020-12-15 12:46:13 +02:00
|
|
|
#define drm_bridge_to_dsi(bridge) \
|
|
|
|
container_of(bridge, struct dsi_data, bridge)
|
|
|
|
|
2018-02-13 14:00:34 +02:00
|
|
|
static inline struct dsi_data *to_dsi_data(struct omap_dss_device *dssdev)
|
2011-05-12 17:26:27 +05:30
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
return dev_get_drvdata(dssdev->dev);
|
2011-05-12 17:26:26 +05:30
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:45 +02:00
|
|
|
static inline struct dsi_data *host_to_omap(struct mipi_dsi_host *host)
|
|
|
|
{
|
|
|
|
return container_of(host, struct dsi_data, host);
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static inline void dsi_write_reg(struct dsi_data *dsi,
|
|
|
|
const struct dsi_reg idx, u32 val)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2013-12-17 13:53:28 +02:00
|
|
|
void __iomem *base;
|
|
|
|
|
|
|
|
switch(idx.module) {
|
|
|
|
case DSI_PROTO: base = dsi->proto_base; break;
|
|
|
|
case DSI_PHY: base = dsi->phy_base; break;
|
|
|
|
case DSI_PLL: base = dsi->pll_base; break;
|
|
|
|
default: return;
|
|
|
|
}
|
2011-05-12 17:26:27 +05:30
|
|
|
|
2013-12-17 13:53:28 +02:00
|
|
|
__raw_writel(val, base + idx.idx);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static inline u32 dsi_read_reg(struct dsi_data *dsi, const struct dsi_reg idx)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2013-12-17 13:53:28 +02:00
|
|
|
void __iomem *base;
|
2011-05-12 17:26:27 +05:30
|
|
|
|
2013-12-17 13:53:28 +02:00
|
|
|
switch(idx.module) {
|
|
|
|
case DSI_PROTO: base = dsi->proto_base; break;
|
|
|
|
case DSI_PHY: base = dsi->phy_base; break;
|
|
|
|
case DSI_PLL: base = dsi->pll_base; break;
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return __raw_readl(base + idx.idx);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:59 +02:00
|
|
|
static void dsi_bus_lock(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
down(&dsi->bus_lock);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:59 +02:00
|
|
|
static void dsi_bus_unlock(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
up(&dsi->bus_lock);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static bool dsi_bus_is_locked(struct dsi_data *dsi)
|
2010-01-18 16:27:52 +02:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
return dsi->bus_lock.count == 0;
|
2010-01-18 16:27:52 +02:00
|
|
|
}
|
|
|
|
|
2011-03-02 14:48:41 +02:00
|
|
|
static void dsi_completion_handler(void *data, u32 mask)
|
|
|
|
{
|
|
|
|
complete((struct completion *)data);
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static inline bool wait_for_bit_change(struct dsi_data *dsi,
|
|
|
|
const struct dsi_reg idx,
|
|
|
|
int bitnum, int value)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-10-13 19:06:49 +03:00
|
|
|
unsigned long timeout;
|
|
|
|
ktime_t wait;
|
|
|
|
int t;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-10-13 19:06:49 +03:00
|
|
|
/* first busyloop to see if the bit changes right away */
|
|
|
|
t = 100;
|
|
|
|
while (t-- > 0) {
|
2018-02-13 14:00:33 +02:00
|
|
|
if (REG_GET(dsi, idx, bitnum, bitnum) == value)
|
2018-02-11 15:07:47 +02:00
|
|
|
return true;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2011-10-13 19:06:49 +03:00
|
|
|
/* then loop for 500ms, sleeping for 1ms in between */
|
|
|
|
timeout = jiffies + msecs_to_jiffies(500);
|
|
|
|
while (time_before(jiffies, timeout)) {
|
2018-02-13 14:00:33 +02:00
|
|
|
if (REG_GET(dsi, idx, bitnum, bitnum) == value)
|
2018-02-11 15:07:47 +02:00
|
|
|
return true;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-10-13 19:06:49 +03:00
|
|
|
wait = ns_to_ktime(1000 * 1000);
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
|
|
schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-11 15:07:47 +02:00
|
|
|
return false;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2013-10-02 14:41:24 +03:00
|
|
|
#ifdef DSI_PERF_MEASURE
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_perf_mark_setup(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
dsi->perf_setup_time = ktime_get();
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_perf_mark_start(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
dsi->perf_start_time = ktime_get();
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_perf_show(struct dsi_data *dsi, const char *name)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
ktime_t t, setup_time, trans_time;
|
|
|
|
u32 total_bytes;
|
|
|
|
u32 setup_us, trans_us, total_us;
|
|
|
|
|
|
|
|
if (!dsi_perf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
t = ktime_get();
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
setup_time = ktime_sub(dsi->perf_start_time, dsi->perf_setup_time);
|
2009-10-28 11:59:56 +02:00
|
|
|
setup_us = (u32)ktime_to_us(setup_time);
|
|
|
|
if (setup_us == 0)
|
|
|
|
setup_us = 1;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
trans_time = ktime_sub(t, dsi->perf_start_time);
|
2009-10-28 11:59:56 +02:00
|
|
|
trans_us = (u32)ktime_to_us(trans_time);
|
|
|
|
if (trans_us == 0)
|
|
|
|
trans_us = 1;
|
|
|
|
|
|
|
|
total_us = setup_us + trans_us;
|
|
|
|
|
2011-11-03 16:34:20 +02:00
|
|
|
total_bytes = dsi->update_bytes;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2017-02-28 04:55:54 -08:00
|
|
|
pr_info("DSI(%s): %u us + %u us = %u us (%uHz), %u bytes, %u kbytes/sec\n",
|
|
|
|
name,
|
|
|
|
setup_us,
|
|
|
|
trans_us,
|
|
|
|
total_us,
|
|
|
|
1000 * 1000 / total_us,
|
|
|
|
total_bytes,
|
|
|
|
total_bytes * 1000 / total_us);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
#else
|
2018-02-13 14:00:33 +02:00
|
|
|
static inline void dsi_perf_mark_setup(struct dsi_data *dsi)
|
2011-05-23 16:36:09 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static inline void dsi_perf_mark_start(struct dsi_data *dsi)
|
2011-05-23 16:36:09 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static inline void dsi_perf_show(struct dsi_data *dsi, const char *name)
|
2011-05-23 16:36:09 +03:00
|
|
|
{
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
#endif
|
|
|
|
|
2012-09-29 12:33:05 +05:30
|
|
|
static int verbose_irq;
|
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
static void print_irq_status(u32 status)
|
|
|
|
{
|
2011-03-02 15:53:07 +02:00
|
|
|
if (status == 0)
|
|
|
|
return;
|
|
|
|
|
2012-09-29 12:33:05 +05:30
|
|
|
if (!verbose_irq && (status & ~DSI_IRQ_CHANNEL_MASK) == 0)
|
2009-10-28 11:59:56 +02:00
|
|
|
return;
|
|
|
|
|
2012-09-29 12:33:05 +05:30
|
|
|
#define PIS(x) (status & DSI_IRQ_##x) ? (#x " ") : ""
|
|
|
|
|
|
|
|
pr_debug("DSI IRQ: 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
|
|
|
status,
|
|
|
|
verbose_irq ? PIS(VC0) : "",
|
|
|
|
verbose_irq ? PIS(VC1) : "",
|
|
|
|
verbose_irq ? PIS(VC2) : "",
|
|
|
|
verbose_irq ? PIS(VC3) : "",
|
|
|
|
PIS(WAKEUP),
|
|
|
|
PIS(RESYNC),
|
|
|
|
PIS(PLL_LOCK),
|
|
|
|
PIS(PLL_UNLOCK),
|
|
|
|
PIS(PLL_RECALL),
|
|
|
|
PIS(COMPLEXIO_ERR),
|
|
|
|
PIS(HS_TX_TIMEOUT),
|
|
|
|
PIS(LP_RX_TIMEOUT),
|
|
|
|
PIS(TE_TRIGGER),
|
|
|
|
PIS(ACK_TRIGGER),
|
|
|
|
PIS(SYNC_LOST),
|
|
|
|
PIS(LDO_POWER_GOOD),
|
|
|
|
PIS(TA_TIMEOUT));
|
|
|
|
#undef PIS
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static void print_irq_status_vc(int vc, u32 status)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-03-02 15:53:07 +02:00
|
|
|
if (status == 0)
|
|
|
|
return;
|
|
|
|
|
2012-09-29 12:33:05 +05:30
|
|
|
if (!verbose_irq && (status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
|
2009-10-28 11:59:56 +02:00
|
|
|
return;
|
2012-09-29 12:33:05 +05:30
|
|
|
|
|
|
|
#define PIS(x) (status & DSI_VC_IRQ_##x) ? (#x " ") : ""
|
|
|
|
|
|
|
|
pr_debug("DSI VC(%d) IRQ 0x%x: %s%s%s%s%s%s%s%s%s\n",
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
vc,
|
2012-09-29 12:33:05 +05:30
|
|
|
status,
|
|
|
|
PIS(CS),
|
|
|
|
PIS(ECC_CORR),
|
|
|
|
PIS(ECC_NO_CORR),
|
|
|
|
verbose_irq ? PIS(PACKET_SENT) : "",
|
|
|
|
PIS(BTA),
|
|
|
|
PIS(FIFO_TX_OVF),
|
|
|
|
PIS(FIFO_RX_OVF),
|
|
|
|
PIS(FIFO_TX_UDF),
|
|
|
|
PIS(PP_BUSY_CHANGE));
|
2009-10-28 11:59:56 +02:00
|
|
|
#undef PIS
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_irq_status_cio(u32 status)
|
|
|
|
{
|
2011-03-02 15:53:07 +02:00
|
|
|
if (status == 0)
|
|
|
|
return;
|
|
|
|
|
2012-09-29 12:33:05 +05:30
|
|
|
#define PIS(x) (status & DSI_CIO_IRQ_##x) ? (#x " ") : ""
|
|
|
|
|
|
|
|
pr_debug("DSI CIO IRQ 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
|
|
|
status,
|
|
|
|
PIS(ERRSYNCESC1),
|
|
|
|
PIS(ERRSYNCESC2),
|
|
|
|
PIS(ERRSYNCESC3),
|
|
|
|
PIS(ERRESC1),
|
|
|
|
PIS(ERRESC2),
|
|
|
|
PIS(ERRESC3),
|
|
|
|
PIS(ERRCONTROL1),
|
|
|
|
PIS(ERRCONTROL2),
|
|
|
|
PIS(ERRCONTROL3),
|
|
|
|
PIS(STATEULPS1),
|
|
|
|
PIS(STATEULPS2),
|
|
|
|
PIS(STATEULPS3),
|
|
|
|
PIS(ERRCONTENTIONLP0_1),
|
|
|
|
PIS(ERRCONTENTIONLP1_1),
|
|
|
|
PIS(ERRCONTENTIONLP0_2),
|
|
|
|
PIS(ERRCONTENTIONLP1_2),
|
|
|
|
PIS(ERRCONTENTIONLP0_3),
|
|
|
|
PIS(ERRCONTENTIONLP1_3),
|
|
|
|
PIS(ULPSACTIVENOT_ALL0),
|
|
|
|
PIS(ULPSACTIVENOT_ALL1));
|
2009-10-28 11:59:56 +02:00
|
|
|
#undef PIS
|
|
|
|
}
|
|
|
|
|
2011-03-02 14:44:27 +02:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_collect_irq_stats(struct dsi_data *dsi, u32 irqstatus,
|
|
|
|
u32 *vcstatus, u32 ciostatus)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock(&dsi->irq_stats_lock);
|
2011-03-02 14:44:27 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
dsi->irq_stats.irq_count++;
|
|
|
|
dss_collect_irq_stats(irqstatus, dsi->irq_stats.dsi_irqs);
|
2011-03-02 14:44:27 +02:00
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i)
|
2011-05-12 17:26:27 +05:30
|
|
|
dss_collect_irq_stats(vcstatus[i], dsi->irq_stats.vc_irqs[i]);
|
2011-03-02 14:44:27 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
dss_collect_irq_stats(ciostatus, dsi->irq_stats.cio_irqs);
|
2011-03-02 14:44:27 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_unlock(&dsi->irq_stats_lock);
|
2011-03-02 14:44:27 +02:00
|
|
|
}
|
|
|
|
#else
|
2018-02-13 14:00:33 +02:00
|
|
|
#define dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus)
|
2009-12-17 14:35:21 +02:00
|
|
|
#endif
|
|
|
|
|
2011-03-02 14:44:27 +02:00
|
|
|
static int debug_irq;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_handle_irq_errors(struct dsi_data *dsi, u32 irqstatus,
|
|
|
|
u32 *vcstatus, u32 ciostatus)
|
2011-03-02 14:44:27 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
if (irqstatus & DSI_IRQ_ERROR_MASK) {
|
|
|
|
DSSERR("DSI error, irqstatus %x\n", irqstatus);
|
|
|
|
print_irq_status(irqstatus);
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock(&dsi->errors_lock);
|
|
|
|
dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK;
|
|
|
|
spin_unlock(&dsi->errors_lock);
|
2009-10-28 11:59:56 +02:00
|
|
|
} else if (debug_irq) {
|
|
|
|
print_irq_status(irqstatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i) {
|
2011-03-02 14:44:27 +02:00
|
|
|
if (vcstatus[i] & DSI_VC_IRQ_ERROR_MASK) {
|
|
|
|
DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
|
|
|
|
i, vcstatus[i]);
|
|
|
|
print_irq_status_vc(i, vcstatus[i]);
|
|
|
|
} else if (debug_irq) {
|
|
|
|
print_irq_status_vc(i, vcstatus[i]);
|
|
|
|
}
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-03-02 14:44:27 +02:00
|
|
|
if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) {
|
|
|
|
DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
|
|
|
|
print_irq_status_cio(ciostatus);
|
|
|
|
} else if (debug_irq) {
|
|
|
|
print_irq_status_cio(ciostatus);
|
|
|
|
}
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-03-02 14:47:04 +02:00
|
|
|
static void dsi_call_isrs(struct dsi_isr_data *isr_array,
|
2018-02-11 15:07:34 +02:00
|
|
|
unsigned int isr_array_size, u32 irqstatus)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
|
|
|
struct dsi_isr_data *isr_data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < isr_array_size; i++) {
|
|
|
|
isr_data = &isr_array[i];
|
|
|
|
if (isr_data->isr && isr_data->mask & irqstatus)
|
|
|
|
isr_data->isr(isr_data->arg, irqstatus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables,
|
|
|
|
u32 irqstatus, u32 *vcstatus, u32 ciostatus)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
dsi_call_isrs(isr_tables->isr_table,
|
|
|
|
ARRAY_SIZE(isr_tables->isr_table),
|
|
|
|
irqstatus);
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
|
|
if (vcstatus[i] == 0)
|
|
|
|
continue;
|
|
|
|
dsi_call_isrs(isr_tables->isr_table_vc[i],
|
|
|
|
ARRAY_SIZE(isr_tables->isr_table_vc[i]),
|
|
|
|
vcstatus[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ciostatus != 0)
|
|
|
|
dsi_call_isrs(isr_tables->isr_table_cio,
|
|
|
|
ARRAY_SIZE(isr_tables->isr_table_cio),
|
|
|
|
ciostatus);
|
|
|
|
}
|
|
|
|
|
2011-03-02 14:44:27 +02:00
|
|
|
static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
|
|
|
|
{
|
2018-02-13 14:00:33 +02:00
|
|
|
struct dsi_data *dsi = arg;
|
2011-03-02 14:44:27 +02:00
|
|
|
u32 irqstatus, vcstatus[4], ciostatus;
|
|
|
|
int i;
|
2009-12-17 14:35:21 +02:00
|
|
|
|
OMAPDSS: fix shared irq handlers
DSS uses shared irq handlers for DISPC and DSI, because on OMAP3, the
DISPC and DSI share the same irq line.
However, the irq handlers presume that the hardware is enabled, which,
in theory, may not be the case with shared irq handlers. So if an
interrupt happens while the DISPC/DSI is off, the kernel will halt as
the irq handler tries to access the DISPC/DSI registers.
In practice that should never happen, as both DSI and DISPC are in the
same power domain. So if there's an IRQ for one of them, the other is
also enabled. However, if CONFIG_DEBUG_SHIRQ is enabled, the kernel will
generate a spurious IRQ, which then causes the problem.
This patch adds an is_enabled field for both DISPC and DSI, which is
used to track if the HW is enabled. For DISPC the code is slightly more
complex, as the users of DISPC can register the interrupt handler, and
we want to hide the is_enabled handling from the users of DISPC.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2014-04-11 13:49:55 +03:00
|
|
|
if (!dsi->is_enabled)
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock(&dsi->irq_lock);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
irqstatus = dsi_read_reg(dsi, DSI_IRQSTATUS);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-03-02 14:44:27 +02:00
|
|
|
/* IRQ is not for us */
|
2011-03-02 14:47:04 +02:00
|
|
|
if (!irqstatus) {
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_unlock(&dsi->irq_lock);
|
2011-03-02 14:44:27 +02:00
|
|
|
return IRQ_NONE;
|
2011-03-02 14:47:04 +02:00
|
|
|
}
|
2010-06-09 15:31:01 +03:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
|
2011-03-02 14:44:27 +02:00
|
|
|
/* flush posted write */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_read_reg(dsi, DSI_IRQSTATUS);
|
2011-03-02 14:44:27 +02:00
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
|
|
if ((irqstatus & (1 << i)) == 0) {
|
|
|
|
vcstatus[i] = 0;
|
|
|
|
continue;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
vcstatus[i] = dsi_read_reg(dsi, DSI_VC_IRQSTATUS(i));
|
2011-03-02 14:44:27 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VC_IRQSTATUS(i), vcstatus[i]);
|
2009-10-28 11:59:56 +02:00
|
|
|
/* flush posted write */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_read_reg(dsi, DSI_VC_IRQSTATUS(i));
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
|
2018-02-13 14:00:33 +02:00
|
|
|
ciostatus = dsi_read_reg(dsi, DSI_COMPLEXIO_IRQ_STATUS);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
|
2009-10-28 11:59:56 +02:00
|
|
|
/* flush posted write */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_read_reg(dsi, DSI_COMPLEXIO_IRQ_STATUS);
|
2011-03-02 14:44:27 +02:00
|
|
|
} else {
|
|
|
|
ciostatus = 0;
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-03-02 14:44:27 +02:00
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
|
|
|
if (irqstatus & DSI_IRQ_TE_TRIGGER)
|
2011-05-12 17:26:27 +05:30
|
|
|
del_timer(&dsi->te_timer);
|
2011-03-02 14:44:27 +02:00
|
|
|
#endif
|
|
|
|
|
2011-03-02 14:47:04 +02:00
|
|
|
/* make a copy and unlock, so that isrs can unregister
|
|
|
|
* themselves */
|
2011-05-12 17:26:27 +05:30
|
|
|
memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
|
|
|
|
sizeof(dsi->isr_tables));
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_unlock(&dsi->irq_lock);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_handle_irq_errors(dsi, irqstatus, vcstatus, ciostatus);
|
2011-03-02 14:44:27 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus);
|
2009-12-17 14:35:21 +02:00
|
|
|
|
2011-02-23 08:41:03 +00:00
|
|
|
return IRQ_HANDLED;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
/* dsi->irq_lock has to be locked by the caller */
|
2018-02-13 14:00:33 +02:00
|
|
|
static void _omap_dsi_configure_irqs(struct dsi_data *dsi,
|
|
|
|
struct dsi_isr_data *isr_array,
|
|
|
|
unsigned int isr_array_size,
|
|
|
|
u32 default_mask,
|
|
|
|
const struct dsi_reg enable_reg,
|
|
|
|
const struct dsi_reg status_reg)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-03-02 14:47:04 +02:00
|
|
|
struct dsi_isr_data *isr_data;
|
|
|
|
u32 mask;
|
|
|
|
u32 old_mask;
|
2009-10-28 11:59:56 +02:00
|
|
|
int i;
|
|
|
|
|
2011-03-02 14:47:04 +02:00
|
|
|
mask = default_mask;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-03-02 14:47:04 +02:00
|
|
|
for (i = 0; i < isr_array_size; i++) {
|
|
|
|
isr_data = &isr_array[i];
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-03-02 14:47:04 +02:00
|
|
|
if (isr_data->isr == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mask |= isr_data->mask;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
old_mask = dsi_read_reg(dsi, enable_reg);
|
2011-03-02 14:47:04 +02:00
|
|
|
/* clear the irqstatus for newly enabled irqs */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, status_reg, (mask ^ old_mask) & mask);
|
|
|
|
dsi_write_reg(dsi, enable_reg, mask);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
/* flush posted writes */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_read_reg(dsi, enable_reg);
|
|
|
|
dsi_read_reg(dsi, status_reg);
|
2011-03-02 14:47:04 +02:00
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
/* dsi->irq_lock has to be locked by the caller */
|
2018-02-13 14:00:33 +02:00
|
|
|
static void _omap_dsi_set_irqs(struct dsi_data *dsi)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
|
|
|
u32 mask = DSI_IRQ_ERROR_MASK;
|
2009-10-28 11:59:56 +02:00
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
2011-03-02 14:47:04 +02:00
|
|
|
mask |= DSI_IRQ_TE_TRIGGER;
|
2009-10-28 11:59:56 +02:00
|
|
|
#endif
|
2018-02-13 14:00:33 +02:00
|
|
|
_omap_dsi_configure_irqs(dsi, dsi->isr_tables.isr_table,
|
2011-05-12 17:26:27 +05:30
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table), mask,
|
2011-03-02 14:47:04 +02:00
|
|
|
DSI_IRQENABLE, DSI_IRQSTATUS);
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
/* dsi->irq_lock has to be locked by the caller */
|
2018-02-13 14:00:33 +02:00
|
|
|
static void _omap_dsi_set_irqs_vc(struct dsi_data *dsi, int vc)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
2018-02-13 14:00:33 +02:00
|
|
|
_omap_dsi_configure_irqs(dsi, dsi->isr_tables.isr_table_vc[vc],
|
2011-05-12 17:26:27 +05:30
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]),
|
2011-03-02 14:47:04 +02:00
|
|
|
DSI_VC_IRQ_ERROR_MASK,
|
|
|
|
DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc));
|
|
|
|
}
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
/* dsi->irq_lock has to be locked by the caller */
|
2018-02-13 14:00:33 +02:00
|
|
|
static void _omap_dsi_set_irqs_cio(struct dsi_data *dsi)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
2018-02-13 14:00:33 +02:00
|
|
|
_omap_dsi_configure_irqs(dsi, dsi->isr_tables.isr_table_cio,
|
2011-05-12 17:26:27 +05:30
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_cio),
|
2011-03-02 14:47:04 +02:00
|
|
|
DSI_CIO_IRQ_ERROR_MASK,
|
|
|
|
DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS);
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void _dsi_initialize_irq(struct dsi_data *dsi)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int vc;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables));
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
_omap_dsi_set_irqs(dsi);
|
2011-03-02 14:47:04 +02:00
|
|
|
for (vc = 0; vc < 4; ++vc)
|
2018-02-13 14:00:33 +02:00
|
|
|
_omap_dsi_set_irqs_vc(dsi, vc);
|
|
|
|
_omap_dsi_set_irqs_cio(dsi);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-03-02 14:47:04 +02:00
|
|
|
static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
|
2018-02-11 15:07:34 +02:00
|
|
|
struct dsi_isr_data *isr_array, unsigned int isr_array_size)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
|
|
|
struct dsi_isr_data *isr_data;
|
|
|
|
int free_idx;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
BUG_ON(isr == NULL);
|
|
|
|
|
|
|
|
/* check for duplicate entry and find a free slot */
|
|
|
|
free_idx = -1;
|
|
|
|
for (i = 0; i < isr_array_size; i++) {
|
|
|
|
isr_data = &isr_array[i];
|
|
|
|
|
|
|
|
if (isr_data->isr == isr && isr_data->arg == arg &&
|
|
|
|
isr_data->mask == mask) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isr_data->isr == NULL && free_idx == -1)
|
|
|
|
free_idx = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (free_idx == -1)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
isr_data = &isr_array[free_idx];
|
|
|
|
isr_data->isr = isr;
|
|
|
|
isr_data->arg = arg;
|
|
|
|
isr_data->mask = mask;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
|
2018-02-11 15:07:34 +02:00
|
|
|
struct dsi_isr_data *isr_array, unsigned int isr_array_size)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
|
|
|
struct dsi_isr_data *isr_data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < isr_array_size; i++) {
|
|
|
|
isr_data = &isr_array[i];
|
|
|
|
if (isr_data->isr != isr || isr_data->arg != arg ||
|
|
|
|
isr_data->mask != mask)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isr_data->isr = NULL;
|
|
|
|
isr_data->arg = NULL;
|
|
|
|
isr_data->mask = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_register_isr(struct dsi_data *dsi, omap_dsi_isr_t isr,
|
|
|
|
void *arg, u32 mask)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table,
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table));
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
if (r == 0)
|
2018-02-13 14:00:33 +02:00
|
|
|
_omap_dsi_set_irqs(dsi);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_unregister_isr(struct dsi_data *dsi, omap_dsi_isr_t isr,
|
|
|
|
void *arg, u32 mask)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table,
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table));
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
if (r == 0)
|
2018-02-13 14:00:33 +02:00
|
|
|
_omap_dsi_set_irqs(dsi);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_register_isr_vc(struct dsi_data *dsi, int vc,
|
2018-02-13 14:00:33 +02:00
|
|
|
omap_dsi_isr_t isr, void *arg, u32 mask)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
r = _dsi_register_isr(isr, arg, mask,
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi->isr_tables.isr_table_vc[vc],
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]));
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
if (r == 0)
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
_omap_dsi_set_irqs_vc(dsi, vc);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_unregister_isr_vc(struct dsi_data *dsi, int vc,
|
2018-02-13 14:00:33 +02:00
|
|
|
omap_dsi_isr_t isr, void *arg, u32 mask)
|
2011-03-02 14:47:04 +02:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
r = _dsi_unregister_isr(isr, arg, mask,
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi->isr_tables.isr_table_vc[vc],
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]));
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
if (r == 0)
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
_omap_dsi_set_irqs_vc(dsi, vc);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 14:47:04 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static u32 dsi_get_errors(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
u32 e;
|
2018-02-13 14:00:33 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock_irqsave(&dsi->errors_lock, flags);
|
|
|
|
e = dsi->errors;
|
|
|
|
dsi->errors = 0;
|
|
|
|
spin_unlock_irqrestore(&dsi->errors_lock, flags);
|
2009-10-28 11:59:56 +02:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_runtime_get(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-05-27 10:52:19 +03:00
|
|
|
int r;
|
|
|
|
|
|
|
|
DSSDBG("dsi_runtime_get\n");
|
|
|
|
|
2018-02-13 14:00:36 +02:00
|
|
|
r = pm_runtime_get_sync(dsi->dev);
|
2020-08-22 14:57:33 +08:00
|
|
|
if (WARN_ON(r < 0)) {
|
|
|
|
pm_runtime_put_noidle(dsi->dev);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
return 0;
|
2011-05-27 10:52:19 +03:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_runtime_put(struct dsi_data *dsi)
|
2011-05-27 10:52:19 +03:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
DSSDBG("dsi_runtime_put\n");
|
|
|
|
|
2018-02-13 14:00:36 +02:00
|
|
|
r = pm_runtime_put_sync(dsi->dev);
|
2012-06-27 16:37:18 +03:00
|
|
|
WARN_ON(r < 0 && r != -ENOSYS);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void _dsi_print_reset_status(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2010-10-07 13:27:42 +03:00
|
|
|
int b0, b1, b2;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* A dummy read using the SCP interface to any DSIPHY register is
|
|
|
|
* required after DSIPHY reset to complete the reset of the DSI complex
|
|
|
|
* I/O. */
|
2020-11-05 14:45:06 +00:00
|
|
|
dsi_read_reg(dsi, DSI_DSIPHY_CFG5);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2017-08-05 01:44:10 +03:00
|
|
|
if (dsi->data->quirks & DSI_QUIRK_REVERSE_TXCLKESC) {
|
2010-10-07 13:27:42 +03:00
|
|
|
b0 = 28;
|
|
|
|
b1 = 27;
|
|
|
|
b2 = 26;
|
|
|
|
} else {
|
|
|
|
b0 = 24;
|
|
|
|
b1 = 25;
|
|
|
|
b2 = 26;
|
|
|
|
}
|
|
|
|
|
2012-09-29 12:33:05 +05:30
|
|
|
#define DSI_FLD_GET(fld, start, end)\
|
2018-02-13 14:00:33 +02:00
|
|
|
FLD_GET(dsi_read_reg(dsi, DSI_##fld), start, end)
|
2012-09-29 12:33:05 +05:30
|
|
|
|
|
|
|
pr_debug("DSI resets: PLL (%d) CIO (%d) PHY (%x%x%x, %d, %d, %d)\n",
|
|
|
|
DSI_FLD_GET(PLL_STATUS, 0, 0),
|
|
|
|
DSI_FLD_GET(COMPLEXIO_CFG1, 29, 29),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, b0, b0),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, b1, b1),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, b2, b2),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, 29, 29),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, 30, 30),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, 31, 31));
|
|
|
|
|
|
|
|
#undef DSI_FLD_GET
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static inline int dsi_if_enable(struct dsi_data *dsi, bool enable)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
DSSDBG("dsi_if_enable(%d)\n", enable);
|
|
|
|
|
|
|
|
enable = enable ? 1 : 0;
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_CTRL, enable, 0, 0); /* IF_EN */
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
if (!wait_for_bit_change(dsi, DSI_CTRL, 0, enable)) {
|
2018-02-11 15:07:47 +02:00
|
|
|
DSSERR("Failed to set dsi_if_enable to %d\n", enable);
|
|
|
|
return -EIO;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static unsigned long dsi_get_pll_hsdiv_dispc_rate(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2014-10-22 14:49:14 +03:00
|
|
|
return dsi->pll.cinfo.clkout[HSDIV_DISPC];
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2014-10-22 14:49:14 +03:00
|
|
|
return dsi->pll.cinfo.clkout[HSDIV_DSI];
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static unsigned long dsi_get_txbyteclkhs(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2014-10-22 14:49:14 +03:00
|
|
|
return dsi->pll.cinfo.clkdco / 16;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static unsigned long dsi_fclk_rate(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
unsigned long r;
|
2018-02-13 14:00:25 +02:00
|
|
|
enum dss_clk_source source;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:25 +02:00
|
|
|
source = dss_get_dsi_clk_source(dsi->dss, dsi->module_id);
|
|
|
|
if (source == DSS_CLK_SRC_FCK) {
|
2011-02-24 14:17:30 +05:30
|
|
|
/* DSI FCLK source is DSS_CLK_FCK */
|
2011-05-27 10:52:19 +03:00
|
|
|
r = clk_get_rate(dsi->dss_clk);
|
2009-10-28 11:59:56 +02:00
|
|
|
} else {
|
2011-02-24 14:17:30 +05:30
|
|
|
/* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_get_pll_hsdiv_dsi_rate(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-08-06 15:45:26 +03:00
|
|
|
static int dsi_lp_clock_calc(unsigned long dsi_fclk,
|
|
|
|
unsigned long lp_clk_min, unsigned long lp_clk_max,
|
|
|
|
struct dsi_lp_clock_info *lp_cinfo)
|
2013-03-05 17:21:35 +02:00
|
|
|
{
|
2018-02-11 15:07:34 +02:00
|
|
|
unsigned int lp_clk_div;
|
2013-03-05 17:21:35 +02:00
|
|
|
unsigned long lp_clk;
|
|
|
|
|
|
|
|
lp_clk_div = DIV_ROUND_UP(dsi_fclk, lp_clk_max * 2);
|
|
|
|
lp_clk = dsi_fclk / 2 / lp_clk_div;
|
|
|
|
|
|
|
|
if (lp_clk < lp_clk_min || lp_clk > lp_clk_max)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-08-06 15:45:26 +03:00
|
|
|
lp_cinfo->lp_clk_div = lp_clk_div;
|
|
|
|
lp_cinfo->lp_clk = lp_clk;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_set_lp_clk_divisor(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
unsigned long dsi_fclk;
|
2018-02-11 15:07:34 +02:00
|
|
|
unsigned int lp_clk_div;
|
2009-10-28 11:59:56 +02:00
|
|
|
unsigned long lp_clk;
|
2018-02-11 15:07:34 +02:00
|
|
|
unsigned int lpdiv_max = dsi->data->max_pll_lpdiv;
|
2014-10-22 14:49:14 +03:00
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2014-08-06 15:45:26 +03:00
|
|
|
lp_clk_div = dsi->user_lp_cinfo.lp_clk_div;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
if (lp_clk_div == 0 || lp_clk_div > lpdiv_max)
|
2009-10-28 11:59:56 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_fclk = dsi_fclk_rate(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
lp_clk = dsi_fclk / 2 / lp_clk_div;
|
|
|
|
|
|
|
|
DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk);
|
2014-08-06 15:45:26 +03:00
|
|
|
dsi->current_lp_cinfo.lp_clk = lp_clk;
|
|
|
|
dsi->current_lp_cinfo.lp_clk_div = lp_clk_div;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:26 +05:30
|
|
|
/* LP_CLK_DIVISOR */
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_CLK_CTRL, lp_clk_div, 12, 0);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:26 +05:30
|
|
|
/* LP_RX_SYNCHRO_ENABLE */
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_enable_scp_clk(struct dsi_data *dsi)
|
2011-04-13 17:12:52 +03:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->scp_clk_refcount++ == 0)
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */
|
2011-04-13 17:12:52 +03:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_disable_scp_clk(struct dsi_data *dsi)
|
2011-04-13 17:12:52 +03:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
WARN_ON(dsi->scp_clk_refcount == 0);
|
|
|
|
if (--dsi->scp_clk_refcount == 0)
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */
|
2011-04-13 17:12:52 +03:00
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
enum dsi_pll_power_state {
|
|
|
|
DSI_PLL_POWER_OFF = 0x0,
|
|
|
|
DSI_PLL_POWER_ON_HSCLK = 0x1,
|
|
|
|
DSI_PLL_POWER_ON_ALL = 0x2,
|
|
|
|
DSI_PLL_POWER_ON_DIV = 0x3,
|
|
|
|
};
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_pll_power(struct dsi_data *dsi, enum dsi_pll_power_state state)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
int t = 0;
|
|
|
|
|
2011-04-15 10:42:59 +03:00
|
|
|
/* DSI-PLL power command 0x3 is not working */
|
2017-08-05 01:44:10 +03:00
|
|
|
if ((dsi->data->quirks & DSI_QUIRK_PLL_PWR_BUG) &&
|
|
|
|
state == DSI_PLL_POWER_ON_DIV)
|
2011-04-15 10:42:59 +03:00
|
|
|
state = DSI_PLL_POWER_ON_ALL;
|
|
|
|
|
2011-05-12 17:26:26 +05:30
|
|
|
/* PLL_PWR_CMD */
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_CLK_CTRL, state, 31, 30);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* PLL_PWR_STATUS */
|
2018-02-13 14:00:33 +02:00
|
|
|
while (FLD_GET(dsi_read_reg(dsi, DSI_CLK_CTRL), 29, 28) != state) {
|
2010-01-07 14:19:48 +02:00
|
|
|
if (++t > 1000) {
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSERR("Failed to set DSI PLL power mode to %d\n",
|
|
|
|
state);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2010-01-07 14:19:48 +02:00
|
|
|
udelay(1);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-05 01:44:15 +03:00
|
|
|
static void dsi_pll_calc_dsi_fck(struct dsi_data *dsi,
|
|
|
|
struct dss_pll_clock_info *cinfo)
|
2012-09-24 15:15:06 +03:00
|
|
|
{
|
|
|
|
unsigned long max_dsi_fck;
|
|
|
|
|
2017-08-05 01:44:15 +03:00
|
|
|
max_dsi_fck = dsi->data->max_fck_freq;
|
2012-09-24 15:15:06 +03:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
cinfo->mX[HSDIV_DSI] = DIV_ROUND_UP(cinfo->clkdco, max_dsi_fck);
|
|
|
|
cinfo->clkout[HSDIV_DSI] = cinfo->clkdco / cinfo->mX[HSDIV_DSI];
|
2012-09-24 15:15:06 +03:00
|
|
|
}
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
static int dsi_pll_enable(struct dss_pll *pll)
|
2014-08-04 13:46:05 +03:00
|
|
|
{
|
2014-10-22 14:49:14 +03:00
|
|
|
struct dsi_data *dsi = container_of(pll, struct dsi_data, pll);
|
2009-10-28 11:59:56 +02:00
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
DSSDBG("PLL init\n");
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_runtime_get(dsi);
|
2014-08-08 10:04:31 +03:00
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
2011-04-13 17:12:52 +03:00
|
|
|
/*
|
|
|
|
* Note: SCP CLK is not required on OMAP3, but it is required on OMAP4.
|
|
|
|
*/
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_enable_scp_clk(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2019-02-07 07:45:16 -08:00
|
|
|
r = regulator_enable(dsi->vdds_dsi_reg);
|
|
|
|
if (r)
|
|
|
|
goto err0;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* XXX PLL does not come out of reset without this... */
|
2018-02-13 14:00:43 +02:00
|
|
|
dispc_pck_free_enable(dsi->dss->dispc, 1);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
if (!wait_for_bit_change(dsi, DSI_PLL_STATUS, 0, 1)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSERR("PLL not coming out of reset.\n");
|
|
|
|
r = -ENODEV;
|
2018-02-13 14:00:43 +02:00
|
|
|
dispc_pck_free_enable(dsi->dss->dispc, 0);
|
2009-10-28 11:59:56 +02:00
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX ... but if left on, we get problems when planes do not
|
|
|
|
* fill the whole display. No idea about this */
|
2018-02-13 14:00:43 +02:00
|
|
|
dispc_pck_free_enable(dsi->dss->dispc, 0);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_pll_power(dsi, DSI_PLL_POWER_ON_ALL);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
if (r)
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
DSSDBG("PLL init done\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
err1:
|
2019-02-07 07:45:16 -08:00
|
|
|
regulator_disable(dsi->vdds_dsi_reg);
|
2009-10-28 11:59:56 +02:00
|
|
|
err0:
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_disable_scp_clk(dsi);
|
|
|
|
dsi_runtime_put(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2019-02-07 07:45:16 -08:00
|
|
|
static void dsi_pll_disable(struct dss_pll *pll)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2019-02-07 07:45:16 -08:00
|
|
|
struct dsi_data *dsi = container_of(pll, struct dsi_data, pll);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_pll_power(dsi, DSI_PLL_POWER_OFF);
|
2019-02-07 07:45:16 -08:00
|
|
|
|
|
|
|
regulator_disable(dsi->vdds_dsi_reg);
|
2011-04-13 17:12:52 +03:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_disable_scp_clk(dsi);
|
|
|
|
dsi_runtime_put(dsi);
|
2011-04-13 17:12:52 +03:00
|
|
|
|
2019-02-07 07:45:16 -08:00
|
|
|
DSSDBG("PLL disable done\n");
|
2014-10-22 14:49:14 +03:00
|
|
|
}
|
|
|
|
|
2018-03-02 00:40:04 +02:00
|
|
|
static int dsi_dump_dsi_clocks(struct seq_file *s, void *p)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2019-01-11 05:50:33 +02:00
|
|
|
struct dsi_data *dsi = s->private;
|
2014-10-22 14:49:14 +03:00
|
|
|
struct dss_pll_clock_info *cinfo = &dsi->pll.cinfo;
|
2016-05-17 13:45:09 +03:00
|
|
|
enum dss_clk_source dispc_clk_src, dsi_clk_src;
|
2012-03-09 16:07:39 +02:00
|
|
|
int dsi_module = dsi->module_id;
|
2014-10-22 14:49:14 +03:00
|
|
|
struct dss_pll *pll = &dsi->pll;
|
2011-03-02 11:57:25 +05:30
|
|
|
|
2018-02-13 14:00:25 +02:00
|
|
|
dispc_clk_src = dss_get_dispc_clk_source(dsi->dss);
|
|
|
|
dsi_clk_src = dss_get_dsi_clk_source(dsi->dss, dsi_module);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
if (dsi_runtime_get(dsi))
|
2018-03-02 00:40:04 +02:00
|
|
|
return 0;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:29 +05:30
|
|
|
seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
seq_printf(s, "dsi pll clkin\t%lu\n", clk_get_rate(pll->clkin));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
seq_printf(s, "Fint\t\t%-16lun %u\n", cinfo->fint, cinfo->n);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
seq_printf(s, "CLKIN4DDR\t%-16lum %u\n",
|
|
|
|
cinfo->clkdco, cinfo->m);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
seq_printf(s, "DSI_PLL_HSDIV_DISPC (%s)\t%-16lum_dispc %u\t(%s)\n",
|
2016-05-17 13:50:55 +03:00
|
|
|
dss_get_clk_source_name(dsi_module == 0 ?
|
2016-05-17 14:01:10 +03:00
|
|
|
DSS_CLK_SRC_PLL1_1 :
|
|
|
|
DSS_CLK_SRC_PLL2_1),
|
2014-11-07 13:13:24 +02:00
|
|
|
cinfo->clkout[HSDIV_DISPC],
|
2014-10-22 14:49:14 +03:00
|
|
|
cinfo->mX[HSDIV_DISPC],
|
2016-05-17 14:01:10 +03:00
|
|
|
dispc_clk_src == DSS_CLK_SRC_FCK ?
|
2010-02-23 17:40:00 +02:00
|
|
|
"off" : "on");
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
seq_printf(s, "DSI_PLL_HSDIV_DSI (%s)\t%-16lum_dsi %u\t(%s)\n",
|
2016-05-17 13:50:55 +03:00
|
|
|
dss_get_clk_source_name(dsi_module == 0 ?
|
2016-05-17 14:01:10 +03:00
|
|
|
DSS_CLK_SRC_PLL1_2 :
|
|
|
|
DSS_CLK_SRC_PLL2_2),
|
2014-11-07 13:13:24 +02:00
|
|
|
cinfo->clkout[HSDIV_DSI],
|
2014-10-22 14:49:14 +03:00
|
|
|
cinfo->mX[HSDIV_DSI],
|
2016-05-17 14:01:10 +03:00
|
|
|
dsi_clk_src == DSS_CLK_SRC_FCK ?
|
2010-02-23 17:40:00 +02:00
|
|
|
"off" : "on");
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:29 +05:30
|
|
|
seq_printf(s, "- DSI%d -\n", dsi_module + 1);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2016-05-17 13:49:18 +03:00
|
|
|
seq_printf(s, "dsi fclk source = %s\n",
|
2016-05-17 13:50:55 +03:00
|
|
|
dss_get_clk_source_name(dsi_clk_src));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsi));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
seq_printf(s, "DDR_CLK\t\t%lu\n",
|
2014-11-07 13:08:16 +02:00
|
|
|
cinfo->clkdco / 4);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsi));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2014-08-06 15:45:26 +03:00
|
|
|
seq_printf(s, "LP_CLK\t\t%lu\n", dsi->current_lp_cinfo.lp_clk);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_runtime_put(dsi);
|
2011-05-12 17:26:29 +05:30
|
|
|
|
2018-03-02 00:40:04 +02:00
|
|
|
return 0;
|
2011-05-12 17:26:29 +05:30
|
|
|
}
|
|
|
|
|
2009-12-17 14:35:21 +02:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
|
2018-03-02 00:40:04 +02:00
|
|
|
static int dsi_dump_dsi_irqs(struct seq_file *s, void *p)
|
2009-12-17 14:35:21 +02:00
|
|
|
{
|
2019-01-11 05:50:33 +02:00
|
|
|
struct dsi_data *dsi = s->private;
|
2009-12-17 14:35:21 +02:00
|
|
|
unsigned long flags;
|
|
|
|
struct dsi_irq_stats stats;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock_irqsave(&dsi->irq_stats_lock, flags);
|
2009-12-17 14:35:21 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
stats = dsi->irq_stats;
|
|
|
|
memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats));
|
|
|
|
dsi->irq_stats.last_reset = jiffies;
|
2009-12-17 14:35:21 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_unlock_irqrestore(&dsi->irq_stats_lock, flags);
|
2009-12-17 14:35:21 +02:00
|
|
|
|
|
|
|
seq_printf(s, "period %u ms\n",
|
|
|
|
jiffies_to_msecs(jiffies - stats.last_reset));
|
|
|
|
|
|
|
|
seq_printf(s, "irqs %d\n", stats.irq_count);
|
|
|
|
#define PIS(x) \
|
|
|
|
seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]);
|
|
|
|
|
2012-03-09 16:07:39 +02:00
|
|
|
seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1);
|
2009-12-17 14:35:21 +02:00
|
|
|
PIS(VC0);
|
|
|
|
PIS(VC1);
|
|
|
|
PIS(VC2);
|
|
|
|
PIS(VC3);
|
|
|
|
PIS(WAKEUP);
|
|
|
|
PIS(RESYNC);
|
|
|
|
PIS(PLL_LOCK);
|
|
|
|
PIS(PLL_UNLOCK);
|
|
|
|
PIS(PLL_RECALL);
|
|
|
|
PIS(COMPLEXIO_ERR);
|
|
|
|
PIS(HS_TX_TIMEOUT);
|
|
|
|
PIS(LP_RX_TIMEOUT);
|
|
|
|
PIS(TE_TRIGGER);
|
|
|
|
PIS(ACK_TRIGGER);
|
|
|
|
PIS(SYNC_LOST);
|
|
|
|
PIS(LDO_POWER_GOOD);
|
|
|
|
PIS(TA_TIMEOUT);
|
|
|
|
#undef PIS
|
|
|
|
|
|
|
|
#define PIS(x) \
|
|
|
|
seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \
|
|
|
|
stats.vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \
|
|
|
|
stats.vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \
|
|
|
|
stats.vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \
|
|
|
|
stats.vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]);
|
|
|
|
|
|
|
|
seq_printf(s, "-- VC interrupts --\n");
|
|
|
|
PIS(CS);
|
|
|
|
PIS(ECC_CORR);
|
|
|
|
PIS(PACKET_SENT);
|
|
|
|
PIS(FIFO_TX_OVF);
|
|
|
|
PIS(FIFO_RX_OVF);
|
|
|
|
PIS(BTA);
|
|
|
|
PIS(ECC_NO_CORR);
|
|
|
|
PIS(FIFO_TX_UDF);
|
|
|
|
PIS(PP_BUSY_CHANGE);
|
|
|
|
#undef PIS
|
|
|
|
|
|
|
|
#define PIS(x) \
|
|
|
|
seq_printf(s, "%-20s %10d\n", #x, \
|
|
|
|
stats.cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]);
|
|
|
|
|
|
|
|
seq_printf(s, "-- CIO interrupts --\n");
|
|
|
|
PIS(ERRSYNCESC1);
|
|
|
|
PIS(ERRSYNCESC2);
|
|
|
|
PIS(ERRSYNCESC3);
|
|
|
|
PIS(ERRESC1);
|
|
|
|
PIS(ERRESC2);
|
|
|
|
PIS(ERRESC3);
|
|
|
|
PIS(ERRCONTROL1);
|
|
|
|
PIS(ERRCONTROL2);
|
|
|
|
PIS(ERRCONTROL3);
|
|
|
|
PIS(STATEULPS1);
|
|
|
|
PIS(STATEULPS2);
|
|
|
|
PIS(STATEULPS3);
|
|
|
|
PIS(ERRCONTENTIONLP0_1);
|
|
|
|
PIS(ERRCONTENTIONLP1_1);
|
|
|
|
PIS(ERRCONTENTIONLP0_2);
|
|
|
|
PIS(ERRCONTENTIONLP1_2);
|
|
|
|
PIS(ERRCONTENTIONLP0_3);
|
|
|
|
PIS(ERRCONTENTIONLP1_3);
|
|
|
|
PIS(ULPSACTIVENOT_ALL0);
|
|
|
|
PIS(ULPSACTIVENOT_ALL1);
|
|
|
|
#undef PIS
|
2011-05-12 17:26:26 +05:30
|
|
|
|
2018-02-13 14:00:29 +02:00
|
|
|
return 0;
|
2011-05-12 17:26:29 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-03-02 00:40:04 +02:00
|
|
|
static int dsi_dump_dsi_regs(struct seq_file *s, void *p)
|
2011-05-12 17:26:29 +05:30
|
|
|
{
|
2019-01-11 05:50:33 +02:00
|
|
|
struct dsi_data *dsi = s->private;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
if (dsi_runtime_get(dsi))
|
2018-03-02 00:40:04 +02:00
|
|
|
return 0;
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_enable_scp_clk(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-03-02 00:40:04 +02:00
|
|
|
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsi, r))
|
2009-10-28 11:59:56 +02:00
|
|
|
DUMPREG(DSI_REVISION);
|
|
|
|
DUMPREG(DSI_SYSCONFIG);
|
|
|
|
DUMPREG(DSI_SYSSTATUS);
|
|
|
|
DUMPREG(DSI_IRQSTATUS);
|
|
|
|
DUMPREG(DSI_IRQENABLE);
|
|
|
|
DUMPREG(DSI_CTRL);
|
|
|
|
DUMPREG(DSI_COMPLEXIO_CFG1);
|
|
|
|
DUMPREG(DSI_COMPLEXIO_IRQ_STATUS);
|
|
|
|
DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE);
|
|
|
|
DUMPREG(DSI_CLK_CTRL);
|
|
|
|
DUMPREG(DSI_TIMING1);
|
|
|
|
DUMPREG(DSI_TIMING2);
|
|
|
|
DUMPREG(DSI_VM_TIMING1);
|
|
|
|
DUMPREG(DSI_VM_TIMING2);
|
|
|
|
DUMPREG(DSI_VM_TIMING3);
|
|
|
|
DUMPREG(DSI_CLK_TIMING);
|
|
|
|
DUMPREG(DSI_TX_FIFO_VC_SIZE);
|
|
|
|
DUMPREG(DSI_RX_FIFO_VC_SIZE);
|
|
|
|
DUMPREG(DSI_COMPLEXIO_CFG2);
|
|
|
|
DUMPREG(DSI_RX_FIFO_VC_FULLNESS);
|
|
|
|
DUMPREG(DSI_VM_TIMING4);
|
|
|
|
DUMPREG(DSI_TX_FIFO_VC_EMPTINESS);
|
|
|
|
DUMPREG(DSI_VM_TIMING5);
|
|
|
|
DUMPREG(DSI_VM_TIMING6);
|
|
|
|
DUMPREG(DSI_VM_TIMING7);
|
|
|
|
DUMPREG(DSI_STOPCLK_TIMING);
|
|
|
|
|
|
|
|
DUMPREG(DSI_VC_CTRL(0));
|
|
|
|
DUMPREG(DSI_VC_TE(0));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_HEADER(0));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0));
|
|
|
|
DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0));
|
|
|
|
DUMPREG(DSI_VC_IRQSTATUS(0));
|
|
|
|
DUMPREG(DSI_VC_IRQENABLE(0));
|
|
|
|
|
|
|
|
DUMPREG(DSI_VC_CTRL(1));
|
|
|
|
DUMPREG(DSI_VC_TE(1));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_HEADER(1));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1));
|
|
|
|
DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1));
|
|
|
|
DUMPREG(DSI_VC_IRQSTATUS(1));
|
|
|
|
DUMPREG(DSI_VC_IRQENABLE(1));
|
|
|
|
|
|
|
|
DUMPREG(DSI_VC_CTRL(2));
|
|
|
|
DUMPREG(DSI_VC_TE(2));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_HEADER(2));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2));
|
|
|
|
DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2));
|
|
|
|
DUMPREG(DSI_VC_IRQSTATUS(2));
|
|
|
|
DUMPREG(DSI_VC_IRQENABLE(2));
|
|
|
|
|
|
|
|
DUMPREG(DSI_VC_CTRL(3));
|
|
|
|
DUMPREG(DSI_VC_TE(3));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_HEADER(3));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3));
|
|
|
|
DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3));
|
|
|
|
DUMPREG(DSI_VC_IRQSTATUS(3));
|
|
|
|
DUMPREG(DSI_VC_IRQENABLE(3));
|
|
|
|
|
|
|
|
DUMPREG(DSI_DSIPHY_CFG0);
|
|
|
|
DUMPREG(DSI_DSIPHY_CFG1);
|
|
|
|
DUMPREG(DSI_DSIPHY_CFG2);
|
|
|
|
DUMPREG(DSI_DSIPHY_CFG5);
|
|
|
|
|
|
|
|
DUMPREG(DSI_PLL_CONTROL);
|
|
|
|
DUMPREG(DSI_PLL_STATUS);
|
|
|
|
DUMPREG(DSI_PLL_GO);
|
|
|
|
DUMPREG(DSI_PLL_CONFIGURATION1);
|
|
|
|
DUMPREG(DSI_PLL_CONFIGURATION2);
|
2018-03-02 00:40:04 +02:00
|
|
|
#undef DUMPREG
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_disable_scp_clk(dsi);
|
|
|
|
dsi_runtime_put(dsi);
|
2011-05-12 17:26:29 +05:30
|
|
|
|
2018-02-13 14:00:29 +02:00
|
|
|
return 0;
|
2011-05-12 17:26:29 +05:30
|
|
|
}
|
|
|
|
|
2010-10-06 15:18:13 +03:00
|
|
|
enum dsi_cio_power_state {
|
2009-10-28 11:59:56 +02:00
|
|
|
DSI_COMPLEXIO_POWER_OFF = 0x0,
|
|
|
|
DSI_COMPLEXIO_POWER_ON = 0x1,
|
|
|
|
DSI_COMPLEXIO_POWER_ULPS = 0x2,
|
|
|
|
};
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_cio_power(struct dsi_data *dsi, enum dsi_cio_power_state state)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
int t = 0;
|
|
|
|
|
|
|
|
/* PWR_CMD */
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_COMPLEXIO_CFG1, state, 28, 27);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* PWR_STATUS */
|
2018-02-13 14:00:33 +02:00
|
|
|
while (FLD_GET(dsi_read_reg(dsi, DSI_COMPLEXIO_CFG1),
|
2011-05-12 17:26:26 +05:30
|
|
|
26, 25) != state) {
|
2010-01-07 14:19:48 +02:00
|
|
|
if (++t > 1000) {
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSERR("failed to set complexio power state to "
|
|
|
|
"%d\n", state);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2010-01-07 14:19:48 +02:00
|
|
|
udelay(1);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static unsigned int dsi_get_line_buf_size(struct dsi_data *dsi)
|
2011-05-16 15:17:09 +05:30
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
|
|
|
/* line buffer on OMAP3 is 1024 x 24bits */
|
|
|
|
/* XXX: for some reason using full buffer size causes
|
|
|
|
* considerable TX slowdown with update sizes that fill the
|
|
|
|
* whole buffer */
|
2017-08-05 01:44:10 +03:00
|
|
|
if (!(dsi->data->quirks & DSI_QUIRK_GNQ))
|
2011-05-16 15:17:09 +05:30
|
|
|
return 1023 * 3;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
val = REG_GET(dsi, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */
|
2011-05-16 15:17:09 +05:30
|
|
|
|
|
|
|
switch (val) {
|
|
|
|
case 1:
|
|
|
|
return 512 * 3; /* 512x24 bits */
|
|
|
|
case 2:
|
|
|
|
return 682 * 3; /* 682x24 bits */
|
|
|
|
case 3:
|
|
|
|
return 853 * 3; /* 853x24 bits */
|
|
|
|
case 4:
|
|
|
|
return 1024 * 3; /* 1024x24 bits */
|
|
|
|
case 5:
|
|
|
|
return 1194 * 3; /* 1194x24 bits */
|
|
|
|
case 6:
|
|
|
|
return 1365 * 3; /* 1365x24 bits */
|
2012-08-22 16:00:47 +03:00
|
|
|
case 7:
|
|
|
|
return 1920 * 3; /* 1920x24 bits */
|
2011-05-16 15:17:09 +05:30
|
|
|
default:
|
|
|
|
BUG();
|
2012-05-18 11:47:02 +03:00
|
|
|
return 0;
|
2011-05-16 15:17:09 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_set_lane_config(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-10-13 11:22:39 +03:00
|
|
|
static const u8 offsets[] = { 0, 4, 8, 12, 16 };
|
|
|
|
static const enum dsi_lane_function functions[] = {
|
|
|
|
DSI_LANE_CLK,
|
|
|
|
DSI_LANE_DATA1,
|
|
|
|
DSI_LANE_DATA2,
|
|
|
|
DSI_LANE_DATA3,
|
|
|
|
DSI_LANE_DATA4,
|
|
|
|
};
|
2009-10-28 11:59:56 +02:00
|
|
|
u32 r;
|
2011-10-13 11:22:39 +03:00
|
|
|
int i;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_COMPLEXIO_CFG1);
|
2011-10-13 11:22:39 +03:00
|
|
|
|
|
|
|
for (i = 0; i < dsi->num_lanes_used; ++i) {
|
2018-02-11 15:07:34 +02:00
|
|
|
unsigned int offset = offsets[i];
|
|
|
|
unsigned int polarity, lane_number;
|
|
|
|
unsigned int t;
|
2011-10-13 11:22:39 +03:00
|
|
|
|
|
|
|
for (t = 0; t < dsi->num_lanes_supported; ++t)
|
|
|
|
if (dsi->lanes[t].function == functions[i])
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (t == dsi->num_lanes_supported)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
lane_number = t;
|
|
|
|
polarity = dsi->lanes[t].polarity;
|
|
|
|
|
|
|
|
r = FLD_MOD(r, lane_number + 1, offset + 2, offset);
|
|
|
|
r = FLD_MOD(r, polarity, offset + 3, offset + 3);
|
2011-05-16 15:17:08 +05:30
|
|
|
}
|
|
|
|
|
2011-10-13 11:22:39 +03:00
|
|
|
/* clear the unused lanes */
|
|
|
|
for (; i < dsi->num_lanes_supported; ++i) {
|
2018-02-11 15:07:34 +02:00
|
|
|
unsigned int offset = offsets[i];
|
2011-10-13 11:22:39 +03:00
|
|
|
|
|
|
|
r = FLD_MOD(r, 0, offset + 2, offset);
|
|
|
|
r = FLD_MOD(r, 0, offset + 3, offset + 3);
|
2011-05-16 15:17:08 +05:30
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_COMPLEXIO_CFG1, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-10-13 11:22:39 +03:00
|
|
|
return 0;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static inline unsigned int ns2ddr(struct dsi_data *dsi, unsigned int ns)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
/* convert time in ns to ddr ticks, rounding up */
|
2014-10-22 14:49:14 +03:00
|
|
|
unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4;
|
2018-02-13 14:00:33 +02:00
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static inline unsigned int ddr2ns(struct dsi_data *dsi, unsigned int ddr)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2014-10-22 14:49:14 +03:00
|
|
|
unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4;
|
2018-02-13 14:00:33 +02:00
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
return ddr * 1000 * 1000 / (ddr_clk / 1000);
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_cio_timings(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
u32 r;
|
|
|
|
u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit;
|
|
|
|
u32 tlpx_half, tclk_trail, tclk_zero;
|
|
|
|
u32 tclk_prepare;
|
|
|
|
|
|
|
|
/* calculate timings */
|
|
|
|
|
|
|
|
/* 1 * DDR_CLK = 2 * UI */
|
|
|
|
|
|
|
|
/* min 40ns + 4*UI max 85ns + 6*UI */
|
2018-02-13 14:00:33 +02:00
|
|
|
ths_prepare = ns2ddr(dsi, 70) + 2;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* min 145ns + 10*UI */
|
2018-02-13 14:00:33 +02:00
|
|
|
ths_prepare_ths_zero = ns2ddr(dsi, 175) + 2;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* min max(8*UI, 60ns+4*UI) */
|
2018-02-13 14:00:33 +02:00
|
|
|
ths_trail = ns2ddr(dsi, 60) + 5;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* min 100ns */
|
2018-02-13 14:00:33 +02:00
|
|
|
ths_exit = ns2ddr(dsi, 145);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* tlpx min 50n */
|
2018-02-13 14:00:33 +02:00
|
|
|
tlpx_half = ns2ddr(dsi, 25);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* min 60ns */
|
2018-02-13 14:00:33 +02:00
|
|
|
tclk_trail = ns2ddr(dsi, 60) + 2;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* min 38ns, max 95ns */
|
2018-02-13 14:00:33 +02:00
|
|
|
tclk_prepare = ns2ddr(dsi, 65);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* min tclk-prepare + tclk-zero = 300ns */
|
2018-02-13 14:00:33 +02:00
|
|
|
tclk_zero = ns2ddr(dsi, 260);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n",
|
2018-02-13 14:00:33 +02:00
|
|
|
ths_prepare, ddr2ns(dsi, ths_prepare),
|
|
|
|
ths_prepare_ths_zero, ddr2ns(dsi, ths_prepare_ths_zero));
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n",
|
2018-02-13 14:00:33 +02:00
|
|
|
ths_trail, ddr2ns(dsi, ths_trail),
|
|
|
|
ths_exit, ddr2ns(dsi, ths_exit));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), "
|
|
|
|
"tclk_zero %u (%uns)\n",
|
2018-02-13 14:00:33 +02:00
|
|
|
tlpx_half, ddr2ns(dsi, tlpx_half),
|
|
|
|
tclk_trail, ddr2ns(dsi, tclk_trail),
|
|
|
|
tclk_zero, ddr2ns(dsi, tclk_zero));
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSDBG("tclk_prepare %u (%uns)\n",
|
2018-02-13 14:00:33 +02:00
|
|
|
tclk_prepare, ddr2ns(dsi, tclk_prepare));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* program timings */
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG0);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, ths_prepare, 31, 24);
|
|
|
|
r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16);
|
|
|
|
r = FLD_MOD(r, ths_trail, 15, 8);
|
|
|
|
r = FLD_MOD(r, ths_exit, 7, 0);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_DSIPHY_CFG0, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1);
|
2012-09-24 09:34:52 +03:00
|
|
|
r = FLD_MOD(r, tlpx_half, 20, 16);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, tclk_trail, 15, 8);
|
|
|
|
r = FLD_MOD(r, tclk_zero, 7, 0);
|
2012-09-24 15:15:57 +03:00
|
|
|
|
2017-08-05 01:44:10 +03:00
|
|
|
if (dsi->data->quirks & DSI_QUIRK_PHY_DCC) {
|
2012-09-24 15:15:57 +03:00
|
|
|
r = FLD_MOD(r, 0, 21, 21); /* DCCEN = disable */
|
|
|
|
r = FLD_MOD(r, 1, 22, 22); /* CLKINP_DIVBY2EN = enable */
|
|
|
|
r = FLD_MOD(r, 1, 23, 23); /* CLKINP_SEL = enable */
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_DSIPHY_CFG1, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG2);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, tclk_prepare, 7, 0);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_DSIPHY_CFG2, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_cio_wait_tx_clk_esc_reset(struct dsi_data *dsi)
|
2010-10-07 13:59:22 +03:00
|
|
|
{
|
2011-10-13 15:26:50 +03:00
|
|
|
int t, i;
|
|
|
|
bool in_use[DSI_MAX_NR_LANES];
|
|
|
|
static const u8 offsets_old[] = { 28, 27, 26 };
|
|
|
|
static const u8 offsets_new[] = { 24, 25, 26, 27, 28 };
|
|
|
|
const u8 *offsets;
|
|
|
|
|
2017-08-05 01:44:10 +03:00
|
|
|
if (dsi->data->quirks & DSI_QUIRK_REVERSE_TXCLKESC)
|
2011-10-13 15:26:50 +03:00
|
|
|
offsets = offsets_old;
|
|
|
|
else
|
|
|
|
offsets = offsets_new;
|
2010-10-07 13:59:22 +03:00
|
|
|
|
2011-10-13 15:26:50 +03:00
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i)
|
|
|
|
in_use[i] = dsi->lanes[i].function != DSI_LANE_UNUSED;
|
2010-10-07 13:59:22 +03:00
|
|
|
|
|
|
|
t = 100000;
|
|
|
|
while (true) {
|
|
|
|
u32 l;
|
|
|
|
int ok;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
l = dsi_read_reg(dsi, DSI_DSIPHY_CFG5);
|
2010-10-07 13:59:22 +03:00
|
|
|
|
|
|
|
ok = 0;
|
2011-10-13 15:26:50 +03:00
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
if (!in_use[i] || (l & (1 << offsets[i])))
|
2010-10-07 13:59:22 +03:00
|
|
|
ok++;
|
|
|
|
}
|
|
|
|
|
2011-10-13 15:26:50 +03:00
|
|
|
if (ok == dsi->num_lanes_supported)
|
2010-10-07 13:59:22 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
if (--t == 0) {
|
2011-10-13 15:26:50 +03:00
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
if (!in_use[i] || (l & (1 << offsets[i])))
|
2010-10-07 13:59:22 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
DSSERR("CIO TXCLKESC%d domain not coming " \
|
|
|
|
"out of reset\n", i);
|
|
|
|
}
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-13 15:12:23 +03:00
|
|
|
/* return bitmask of enabled lanes, lane0 being the lsb */
|
2018-02-13 14:00:33 +02:00
|
|
|
static unsigned int dsi_get_lane_mask(struct dsi_data *dsi)
|
2011-06-15 15:21:12 +03:00
|
|
|
{
|
2018-02-11 15:07:34 +02:00
|
|
|
unsigned int mask = 0;
|
2011-10-13 15:12:23 +03:00
|
|
|
int i;
|
2011-06-15 15:21:12 +03:00
|
|
|
|
2011-10-13 15:12:23 +03:00
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
if (dsi->lanes[i].function != DSI_LANE_UNUSED)
|
|
|
|
mask |= 1 << i;
|
|
|
|
}
|
2011-06-15 15:21:12 +03:00
|
|
|
|
2011-10-13 15:12:23 +03:00
|
|
|
return mask;
|
2011-06-15 15:21:12 +03:00
|
|
|
}
|
|
|
|
|
2017-08-05 01:43:53 +03:00
|
|
|
/* OMAP4 CONTROL_DSIPHY */
|
|
|
|
#define OMAP4_DSIPHY_SYSCON_OFFSET 0x78
|
|
|
|
|
|
|
|
#define OMAP4_DSI2_LANEENABLE_SHIFT 29
|
|
|
|
#define OMAP4_DSI2_LANEENABLE_MASK (0x7 << 29)
|
|
|
|
#define OMAP4_DSI1_LANEENABLE_SHIFT 24
|
|
|
|
#define OMAP4_DSI1_LANEENABLE_MASK (0x1f << 24)
|
|
|
|
#define OMAP4_DSI1_PIPD_SHIFT 19
|
|
|
|
#define OMAP4_DSI1_PIPD_MASK (0x1f << 19)
|
|
|
|
#define OMAP4_DSI2_PIPD_SHIFT 14
|
|
|
|
#define OMAP4_DSI2_PIPD_MASK (0x1f << 14)
|
|
|
|
|
|
|
|
static int dsi_omap4_mux_pads(struct dsi_data *dsi, unsigned int lanes)
|
|
|
|
{
|
|
|
|
u32 enable_mask, enable_shift;
|
|
|
|
u32 pipd_mask, pipd_shift;
|
|
|
|
|
|
|
|
if (dsi->module_id == 0) {
|
|
|
|
enable_mask = OMAP4_DSI1_LANEENABLE_MASK;
|
|
|
|
enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT;
|
|
|
|
pipd_mask = OMAP4_DSI1_PIPD_MASK;
|
|
|
|
pipd_shift = OMAP4_DSI1_PIPD_SHIFT;
|
|
|
|
} else if (dsi->module_id == 1) {
|
|
|
|
enable_mask = OMAP4_DSI2_LANEENABLE_MASK;
|
|
|
|
enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT;
|
|
|
|
pipd_mask = OMAP4_DSI2_PIPD_MASK;
|
|
|
|
pipd_shift = OMAP4_DSI2_PIPD_SHIFT;
|
|
|
|
} else {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2017-08-10 15:11:03 +03:00
|
|
|
return regmap_update_bits(dsi->syscon, OMAP4_DSIPHY_SYSCON_OFFSET,
|
|
|
|
enable_mask | pipd_mask,
|
|
|
|
(lanes << enable_shift) | (lanes << pipd_shift));
|
2017-08-05 01:43:53 +03:00
|
|
|
}
|
|
|
|
|
2017-08-10 09:33:07 +03:00
|
|
|
/* OMAP5 CONTROL_DSIPHY */
|
|
|
|
|
|
|
|
#define OMAP5_DSIPHY_SYSCON_OFFSET 0x74
|
|
|
|
|
|
|
|
#define OMAP5_DSI1_LANEENABLE_SHIFT 24
|
|
|
|
#define OMAP5_DSI2_LANEENABLE_SHIFT 19
|
|
|
|
#define OMAP5_DSI_LANEENABLE_MASK 0x1f
|
|
|
|
|
|
|
|
static int dsi_omap5_mux_pads(struct dsi_data *dsi, unsigned int lanes)
|
|
|
|
{
|
|
|
|
u32 enable_shift;
|
|
|
|
|
|
|
|
if (dsi->module_id == 0)
|
|
|
|
enable_shift = OMAP5_DSI1_LANEENABLE_SHIFT;
|
|
|
|
else if (dsi->module_id == 1)
|
|
|
|
enable_shift = OMAP5_DSI2_LANEENABLE_SHIFT;
|
|
|
|
else
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return regmap_update_bits(dsi->syscon, OMAP5_DSIPHY_SYSCON_OFFSET,
|
|
|
|
OMAP5_DSI_LANEENABLE_MASK << enable_shift,
|
|
|
|
lanes << enable_shift);
|
|
|
|
}
|
|
|
|
|
2017-08-05 01:43:53 +03:00
|
|
|
static int dsi_enable_pads(struct dsi_data *dsi, unsigned int lane_mask)
|
|
|
|
{
|
2017-08-10 09:33:07 +03:00
|
|
|
if (dsi->data->model == DSI_MODEL_OMAP4)
|
|
|
|
return dsi_omap4_mux_pads(dsi, lane_mask);
|
|
|
|
if (dsi->data->model == DSI_MODEL_OMAP5)
|
|
|
|
return dsi_omap5_mux_pads(dsi, lane_mask);
|
|
|
|
return 0;
|
2017-08-05 01:43:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_disable_pads(struct dsi_data *dsi)
|
|
|
|
{
|
2017-08-10 09:33:07 +03:00
|
|
|
if (dsi->data->model == DSI_MODEL_OMAP4)
|
|
|
|
dsi_omap4_mux_pads(dsi, 0);
|
|
|
|
else if (dsi->data->model == DSI_MODEL_OMAP5)
|
|
|
|
dsi_omap5_mux_pads(dsi, 0);
|
2017-08-05 01:43:53 +03:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_cio_init(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-04-15 11:58:41 +03:00
|
|
|
int r;
|
2010-07-28 15:53:38 +03:00
|
|
|
u32 l;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2012-09-24 17:12:58 +05:30
|
|
|
DSSDBG("DSI CIO init starts");
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_enable_pads(dsi, dsi_get_lane_mask(dsi));
|
2011-06-15 15:21:12 +03:00
|
|
|
if (r)
|
|
|
|
return r;
|
2010-07-30 11:57:57 +03:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_enable_scp_clk(dsi);
|
2010-07-28 15:53:38 +03:00
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
/* A dummy read using the SCP interface to any DSIPHY register is
|
|
|
|
* required after DSIPHY reset to complete the reset of the DSI complex
|
|
|
|
* I/O. */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_read_reg(dsi, DSI_DSIPHY_CFG5);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
if (!wait_for_bit_change(dsi, DSI_DSIPHY_CFG5, 30, 1)) {
|
2011-04-15 11:58:41 +03:00
|
|
|
DSSERR("CIO SCP Clock domain not coming out of reset.\n");
|
|
|
|
r = -EIO;
|
|
|
|
goto err_scp_clk_dom;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_set_lane_config(dsi);
|
2011-10-13 11:22:39 +03:00
|
|
|
if (r)
|
|
|
|
goto err_scp_clk_dom;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-07-28 15:53:38 +03:00
|
|
|
/* set TX STOP MODE timer to maximum for this operation */
|
2018-02-13 14:00:33 +02:00
|
|
|
l = dsi_read_reg(dsi, DSI_TIMING1);
|
2010-07-28 15:53:38 +03:00
|
|
|
l = FLD_MOD(l, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
|
|
|
|
l = FLD_MOD(l, 1, 14, 14); /* STOP_STATE_X16_IO */
|
|
|
|
l = FLD_MOD(l, 1, 13, 13); /* STOP_STATE_X4_IO */
|
|
|
|
l = FLD_MOD(l, 0x1fff, 12, 0); /* STOP_STATE_COUNTER_IO */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_TIMING1, l);
|
2010-07-28 15:53:38 +03:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_cio_power(dsi, DSI_COMPLEXIO_POWER_ON);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (r)
|
2011-04-15 11:58:41 +03:00
|
|
|
goto err_cio_pwr;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
if (!wait_for_bit_change(dsi, DSI_COMPLEXIO_CFG1, 29, 1)) {
|
2011-04-15 11:58:41 +03:00
|
|
|
DSSERR("CIO PWR clock domain not coming out of reset.\n");
|
|
|
|
r = -ENODEV;
|
|
|
|
goto err_cio_pwr_dom;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_if_enable(dsi, true);
|
|
|
|
dsi_if_enable(dsi, false);
|
|
|
|
REG_FLD_MOD(dsi, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_cio_wait_tx_clk_esc_reset(dsi);
|
2010-10-07 13:59:22 +03:00
|
|
|
if (r)
|
|
|
|
goto err_tx_clk_esc_rst;
|
|
|
|
|
2010-07-28 15:53:38 +03:00
|
|
|
/* FORCE_TX_STOP_MODE_IO */
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_TIMING1, 0, 15, 15);
|
2010-07-28 15:53:38 +03:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_cio_timings(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:54 +02:00
|
|
|
/* DDR_CLK_ALWAYS_ON */
|
|
|
|
REG_FLD_MOD(dsi, DSI_CLK_CTRL,
|
|
|
|
!(dsi->dsidev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS),
|
|
|
|
13, 13);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSDBG("CIO init done\n");
|
2011-04-15 11:58:41 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2010-10-07 13:59:22 +03:00
|
|
|
err_tx_clk_esc_rst:
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */
|
2011-04-15 11:58:41 +03:00
|
|
|
err_cio_pwr_dom:
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_cio_power(dsi, DSI_COMPLEXIO_POWER_OFF);
|
2011-04-15 11:58:41 +03:00
|
|
|
err_cio_pwr:
|
|
|
|
err_scp_clk_dom:
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_disable_scp_clk(dsi);
|
2017-08-05 01:43:53 +03:00
|
|
|
dsi_disable_pads(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_cio_uninit(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-09-05 16:48:27 +05:30
|
|
|
/* DDR_CLK_ALWAYS_ON */
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_CLK_CTRL, 0, 13, 13);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_cio_power(dsi, DSI_COMPLEXIO_POWER_OFF);
|
|
|
|
dsi_disable_scp_clk(dsi);
|
2017-08-05 01:43:53 +03:00
|
|
|
dsi_disable_pads(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_config_tx_fifo(struct dsi_data *dsi,
|
|
|
|
enum fifo_size size1, enum fifo_size size2,
|
|
|
|
enum fifo_size size3, enum fifo_size size4)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
u32 r = 0;
|
|
|
|
int add = 0;
|
|
|
|
int i;
|
|
|
|
|
2013-09-25 14:40:06 +03:00
|
|
|
dsi->vc[0].tx_fifo_size = size1;
|
|
|
|
dsi->vc[1].tx_fifo_size = size2;
|
|
|
|
dsi->vc[2].tx_fifo_size = size3;
|
|
|
|
dsi->vc[3].tx_fifo_size = size4;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
u8 v;
|
2013-09-25 14:40:06 +03:00
|
|
|
int size = dsi->vc[i].tx_fifo_size;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
if (add + size > 4) {
|
|
|
|
DSSERR("Illegal FIFO configuration\n");
|
|
|
|
BUG();
|
2012-05-18 11:47:02 +03:00
|
|
|
return;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
|
|
|
|
r |= v << (8 * i);
|
|
|
|
/*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */
|
|
|
|
add += size;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_TX_FIFO_VC_SIZE, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_config_rx_fifo(struct dsi_data *dsi,
|
2011-05-12 17:26:26 +05:30
|
|
|
enum fifo_size size1, enum fifo_size size2,
|
2009-10-28 11:59:56 +02:00
|
|
|
enum fifo_size size3, enum fifo_size size4)
|
|
|
|
{
|
|
|
|
u32 r = 0;
|
|
|
|
int add = 0;
|
|
|
|
int i;
|
|
|
|
|
2013-09-25 14:40:06 +03:00
|
|
|
dsi->vc[0].rx_fifo_size = size1;
|
|
|
|
dsi->vc[1].rx_fifo_size = size2;
|
|
|
|
dsi->vc[2].rx_fifo_size = size3;
|
|
|
|
dsi->vc[3].rx_fifo_size = size4;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
u8 v;
|
2013-09-25 14:40:06 +03:00
|
|
|
int size = dsi->vc[i].rx_fifo_size;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
if (add + size > 4) {
|
|
|
|
DSSERR("Illegal FIFO configuration\n");
|
|
|
|
BUG();
|
2012-05-18 11:47:02 +03:00
|
|
|
return;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
|
|
|
|
r |= v << (8 * i);
|
|
|
|
/*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */
|
|
|
|
add += size;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_RX_FIFO_VC_SIZE, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_force_tx_stop_mode_io(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
u32 r;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_TIMING1);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_TIMING1, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
if (!wait_for_bit_change(dsi, DSI_TIMING1, 15, 0)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSERR("TX_STOP bit not going down\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static bool dsi_vc_is_enabled(struct dsi_data *dsi, int vc)
|
2011-03-23 09:59:34 +00:00
|
|
|
{
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
return REG_GET(dsi, DSI_VC_CTRL(vc), 0, 0);
|
2011-03-23 09:59:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_packet_sent_handler_vp(void *data, u32 mask)
|
|
|
|
{
|
2011-05-12 17:26:28 +05:30
|
|
|
struct dsi_packet_sent_handler_data *vp_data =
|
|
|
|
(struct dsi_packet_sent_handler_data *) data;
|
2018-02-13 14:00:33 +02:00
|
|
|
struct dsi_data *dsi = vp_data->dsi;
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
const int vc = dsi->update_vc;
|
2011-05-12 17:26:27 +05:30
|
|
|
u8 bit = dsi->te_enabled ? 30 : 31;
|
2011-03-23 09:59:34 +00:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
if (REG_GET(dsi, DSI_VC_TE(vc), bit, bit) == 0)
|
2011-05-12 17:26:28 +05:30
|
|
|
complete(vp_data->completion);
|
2011-03-23 09:59:34 +00:00
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_sync_vc_vp(struct dsi_data *dsi, int vc)
|
2011-03-23 09:59:34 +00:00
|
|
|
{
|
2011-05-12 17:26:28 +05:30
|
|
|
DECLARE_COMPLETION_ONSTACK(completion);
|
2014-08-23 13:20:29 +02:00
|
|
|
struct dsi_packet_sent_handler_data vp_data = {
|
2018-02-13 14:00:33 +02:00
|
|
|
.dsi = dsi,
|
2014-08-23 13:20:29 +02:00
|
|
|
.completion = &completion
|
|
|
|
};
|
2011-03-23 09:59:34 +00:00
|
|
|
int r = 0;
|
|
|
|
u8 bit;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
bit = dsi->te_enabled ? 30 : 31;
|
2011-03-23 09:59:34 +00:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
r = dsi_register_isr_vc(dsi, vc, dsi_packet_sent_handler_vp,
|
2011-05-12 17:26:28 +05:30
|
|
|
&vp_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 09:59:34 +00:00
|
|
|
if (r)
|
|
|
|
goto err0;
|
|
|
|
|
|
|
|
/* Wait for completion only if TE_EN/TE_START is still set */
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
if (REG_GET(dsi, DSI_VC_TE(vc), bit, bit)) {
|
2011-03-23 09:59:34 +00:00
|
|
|
if (wait_for_completion_timeout(&completion,
|
|
|
|
msecs_to_jiffies(10)) == 0) {
|
|
|
|
DSSERR("Failed to complete previous frame transfer\n");
|
|
|
|
r = -EIO;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_vp,
|
2011-05-12 17:26:28 +05:30
|
|
|
&vp_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 09:59:34 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
err1:
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_vp,
|
2011-05-12 17:26:28 +05:30
|
|
|
&vp_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 09:59:34 +00:00
|
|
|
err0:
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_packet_sent_handler_l4(void *data, u32 mask)
|
|
|
|
{
|
2011-05-12 17:26:28 +05:30
|
|
|
struct dsi_packet_sent_handler_data *l4_data =
|
|
|
|
(struct dsi_packet_sent_handler_data *) data;
|
2018-02-13 14:00:33 +02:00
|
|
|
struct dsi_data *dsi = l4_data->dsi;
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
const int vc = dsi->update_vc;
|
2011-03-23 09:59:34 +00:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
if (REG_GET(dsi, DSI_VC_CTRL(vc), 5, 5) == 0)
|
2011-05-12 17:26:28 +05:30
|
|
|
complete(l4_data->completion);
|
2011-03-23 09:59:34 +00:00
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_sync_vc_l4(struct dsi_data *dsi, int vc)
|
2011-03-23 09:59:34 +00:00
|
|
|
{
|
|
|
|
DECLARE_COMPLETION_ONSTACK(completion);
|
2014-08-23 13:20:29 +02:00
|
|
|
struct dsi_packet_sent_handler_data l4_data = {
|
2018-02-13 14:00:33 +02:00
|
|
|
.dsi = dsi,
|
2014-08-23 13:20:29 +02:00
|
|
|
.completion = &completion
|
|
|
|
};
|
2011-05-12 17:26:28 +05:30
|
|
|
int r = 0;
|
2011-03-23 09:59:34 +00:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
r = dsi_register_isr_vc(dsi, vc, dsi_packet_sent_handler_l4,
|
2011-05-12 17:26:28 +05:30
|
|
|
&l4_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 09:59:34 +00:00
|
|
|
if (r)
|
|
|
|
goto err0;
|
|
|
|
|
|
|
|
/* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
if (REG_GET(dsi, DSI_VC_CTRL(vc), 5, 5)) {
|
2011-03-23 09:59:34 +00:00
|
|
|
if (wait_for_completion_timeout(&completion,
|
|
|
|
msecs_to_jiffies(10)) == 0) {
|
|
|
|
DSSERR("Failed to complete previous l4 transfer\n");
|
|
|
|
r = -EIO;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_l4,
|
2011-05-12 17:26:28 +05:30
|
|
|
&l4_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 09:59:34 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
err1:
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_l4,
|
2011-05-12 17:26:28 +05:30
|
|
|
&l4_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 09:59:34 +00:00
|
|
|
err0:
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_sync_vc(struct dsi_data *dsi, int vc)
|
2011-03-23 09:59:34 +00:00
|
|
|
{
|
2018-02-13 14:00:33 +02:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsi));
|
2011-03-23 09:59:34 +00:00
|
|
|
|
|
|
|
WARN_ON(in_interrupt());
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
if (!dsi_vc_is_enabled(dsi, vc))
|
2011-03-23 09:59:34 +00:00
|
|
|
return 0;
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
switch (dsi->vc[vc].source) {
|
2011-08-22 11:58:08 +05:30
|
|
|
case DSI_VC_SOURCE_VP:
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
return dsi_sync_vc_vp(dsi, vc);
|
2011-08-22 11:58:08 +05:30
|
|
|
case DSI_VC_SOURCE_L4:
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
return dsi_sync_vc_l4(dsi, vc);
|
2011-03-23 09:59:34 +00:00
|
|
|
default:
|
|
|
|
BUG();
|
2012-05-18 11:47:02 +03:00
|
|
|
return -EINVAL;
|
2011-03-23 09:59:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_vc_enable(struct dsi_data *dsi, int vc, bool enable)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
DSSDBG("dsi_vc_enable vc %d, enable %d\n",
|
|
|
|
vc, enable);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
enable = enable ? 1 : 0;
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), enable, 0, 0);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
if (!wait_for_bit_change(dsi, DSI_VC_CTRL(vc), 0, enable)) {
|
2018-02-11 15:07:47 +02:00
|
|
|
DSSERR("Failed to set dsi_vc_enable to %d\n", enable);
|
|
|
|
return -EIO;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static void dsi_vc_initial_config(struct dsi_data *dsi, int vc)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
u32 r;
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
DSSDBG("Initial config of VC %d", vc);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_VC_CTRL(vc));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
if (FLD_GET(r, 15, 15)) /* VC_BUSY */
|
|
|
|
DSSERR("VC(%d) busy when trying to configure it!\n",
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
vc);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */
|
|
|
|
r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */
|
|
|
|
r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
|
|
|
|
r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
|
|
|
|
r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
|
|
|
|
r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
|
|
|
|
r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
|
2017-08-05 01:44:10 +03:00
|
|
|
if (dsi->data->quirks & DSI_QUIRK_VC_OCP_WIDTH)
|
2011-03-22 06:33:36 -05:00
|
|
|
r = FLD_MOD(r, 3, 11, 10); /* OCP_WIDTH = 32 bit */
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
|
|
|
|
r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VC_CTRL(vc), r);
|
2013-02-22 13:42:59 +02:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi->vc[vc].source = DSI_VC_SOURCE_L4;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static void dsi_vc_enable_hs(struct omap_dss_device *dssdev, int vc,
|
2011-05-12 17:26:24 +05:30
|
|
|
bool enable)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
2011-05-12 17:26:26 +05:30
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
DSSDBG("dsi_vc_enable_hs(%d, %d)\n", vc, enable);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:41 +02:00
|
|
|
if (REG_GET(dsi, DSI_VC_CTRL(vc), 9, 9) == enable)
|
|
|
|
return;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsi));
|
2010-01-12 16:00:30 +02:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_vc_enable(dsi, vc, 0);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_if_enable(dsi, 0);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), enable, 9, 9);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_vc_enable(dsi, vc, 1);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_if_enable(dsi, 1);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_force_tx_stop_mode_io(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static void dsi_vc_flush_long_data(struct dsi_data *dsi, int vc)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
while (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
u32 val;
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc));
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n",
|
|
|
|
(val >> 0) & 0xff,
|
|
|
|
(val >> 8) & 0xff,
|
|
|
|
(val >> 16) & 0xff,
|
|
|
|
(val >> 24) & 0xff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_show_rx_ack_with_err(u16 err)
|
|
|
|
{
|
|
|
|
DSSERR("\tACK with ERROR (%#x):\n", err);
|
|
|
|
if (err & (1 << 0))
|
|
|
|
DSSERR("\t\tSoT Error\n");
|
|
|
|
if (err & (1 << 1))
|
|
|
|
DSSERR("\t\tSoT Sync Error\n");
|
|
|
|
if (err & (1 << 2))
|
|
|
|
DSSERR("\t\tEoT Sync Error\n");
|
|
|
|
if (err & (1 << 3))
|
|
|
|
DSSERR("\t\tEscape Mode Entry Command Error\n");
|
|
|
|
if (err & (1 << 4))
|
|
|
|
DSSERR("\t\tLP Transmit Sync Error\n");
|
|
|
|
if (err & (1 << 5))
|
|
|
|
DSSERR("\t\tHS Receive Timeout Error\n");
|
|
|
|
if (err & (1 << 6))
|
|
|
|
DSSERR("\t\tFalse Control Error\n");
|
|
|
|
if (err & (1 << 7))
|
|
|
|
DSSERR("\t\t(reserved7)\n");
|
|
|
|
if (err & (1 << 8))
|
|
|
|
DSSERR("\t\tECC Error, single-bit (corrected)\n");
|
|
|
|
if (err & (1 << 9))
|
|
|
|
DSSERR("\t\tECC Error, multi-bit (not corrected)\n");
|
|
|
|
if (err & (1 << 10))
|
|
|
|
DSSERR("\t\tChecksum Error\n");
|
|
|
|
if (err & (1 << 11))
|
|
|
|
DSSERR("\t\tData type not recognized\n");
|
|
|
|
if (err & (1 << 12))
|
|
|
|
DSSERR("\t\tInvalid VC ID\n");
|
|
|
|
if (err & (1 << 13))
|
|
|
|
DSSERR("\t\tInvalid Transmission Length\n");
|
|
|
|
if (err & (1 << 14))
|
|
|
|
DSSERR("\t\t(reserved14)\n");
|
|
|
|
if (err & (1 << 15))
|
|
|
|
DSSERR("\t\tDSI Protocol Violation\n");
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static u16 dsi_vc_flush_receive_data(struct dsi_data *dsi, int vc)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
/* RX_FIFO_NOT_EMPTY */
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
while (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
u32 val;
|
|
|
|
u8 dt;
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc));
|
2010-03-16 16:19:06 +02:00
|
|
|
DSSERR("\trawval %#08x\n", val);
|
2009-10-28 11:59:56 +02:00
|
|
|
dt = FLD_GET(val, 5, 0);
|
2011-08-25 18:25:03 +05:30
|
|
|
if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
|
2009-10-28 11:59:56 +02:00
|
|
|
u16 err = FLD_GET(val, 23, 8);
|
|
|
|
dsi_show_rx_ack_with_err(err);
|
2011-08-25 18:25:03 +05:30
|
|
|
} else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) {
|
2010-03-16 16:19:06 +02:00
|
|
|
DSSERR("\tDCS short response, 1 byte: %#x\n",
|
2009-10-28 11:59:56 +02:00
|
|
|
FLD_GET(val, 23, 8));
|
2011-08-25 18:25:03 +05:30
|
|
|
} else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) {
|
2010-03-16 16:19:06 +02:00
|
|
|
DSSERR("\tDCS short response, 2 byte: %#x\n",
|
2009-10-28 11:59:56 +02:00
|
|
|
FLD_GET(val, 23, 8));
|
2011-08-25 18:25:03 +05:30
|
|
|
} else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) {
|
2010-03-16 16:19:06 +02:00
|
|
|
DSSERR("\tDCS long response, len %d\n",
|
2009-10-28 11:59:56 +02:00
|
|
|
FLD_GET(val, 23, 8));
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_vc_flush_long_data(dsi, vc);
|
2009-10-28 11:59:56 +02:00
|
|
|
} else {
|
|
|
|
DSSERR("\tunknown datatype 0x%02x\n", dt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_vc_send_bta(struct dsi_data *dsi, int vc)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_write || dsi->debug_read)
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
DSSDBG("dsi_vc_send_bta %d\n", vc);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsi));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:26 +05:30
|
|
|
/* RX_FIFO_NOT_EMPTY */
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_vc_flush_receive_data(dsi, vc);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), 1, 6, 6); /* BTA_EN */
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-10-12 10:13:14 +03:00
|
|
|
/* flush posted write */
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_read_reg(dsi, DSI_VC_CTRL(vc));
|
2011-10-12 10:13:14 +03:00
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int vc)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
2011-03-02 14:48:41 +02:00
|
|
|
DECLARE_COMPLETION_ONSTACK(completion);
|
2009-10-28 11:59:56 +02:00
|
|
|
int r = 0;
|
|
|
|
u32 err;
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
r = dsi_register_isr_vc(dsi, vc, dsi_completion_handler,
|
2011-03-02 14:48:41 +02:00
|
|
|
&completion, DSI_VC_IRQ_BTA);
|
|
|
|
if (r)
|
|
|
|
goto err0;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_register_isr(dsi, dsi_completion_handler, &completion,
|
2010-10-08 16:15:25 +03:00
|
|
|
DSI_IRQ_ERROR_MASK);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (r)
|
2011-03-02 14:48:41 +02:00
|
|
|
goto err1;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
r = dsi_vc_send_bta(dsi, vc);
|
2010-10-08 16:15:25 +03:00
|
|
|
if (r)
|
|
|
|
goto err2;
|
|
|
|
|
2011-03-02 14:48:41 +02:00
|
|
|
if (wait_for_completion_timeout(&completion,
|
2009-10-28 11:59:56 +02:00
|
|
|
msecs_to_jiffies(500)) == 0) {
|
|
|
|
DSSERR("Failed to receive BTA\n");
|
|
|
|
r = -EIO;
|
2010-10-08 16:15:25 +03:00
|
|
|
goto err2;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
err = dsi_get_errors(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (err) {
|
|
|
|
DSSERR("Error while sending BTA: %x\n", err);
|
|
|
|
r = -EIO;
|
2010-10-08 16:15:25 +03:00
|
|
|
goto err2;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
2010-10-08 16:15:25 +03:00
|
|
|
err2:
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_unregister_isr(dsi, dsi_completion_handler, &completion,
|
2010-10-08 16:15:25 +03:00
|
|
|
DSI_IRQ_ERROR_MASK);
|
2011-03-02 14:48:41 +02:00
|
|
|
err1:
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_unregister_isr_vc(dsi, vc, dsi_completion_handler,
|
2011-03-02 14:48:41 +02:00
|
|
|
&completion, DSI_VC_IRQ_BTA);
|
|
|
|
err0:
|
2009-10-28 11:59:56 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static inline void dsi_vc_write_long_header(struct dsi_data *dsi, int vc,
|
2020-12-15 12:46:39 +02:00
|
|
|
int channel, u8 data_type, u16 len,
|
|
|
|
u8 ecc)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
u8 data_id;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsi));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
data_id = data_type | channel << 6;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
|
|
|
|
FLD_VAL(ecc, 31, 24);
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VC_LONG_PACKET_HEADER(vc), val);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static inline void dsi_vc_write_long_payload(struct dsi_data *dsi, int vc,
|
2018-02-13 14:00:33 +02:00
|
|
|
u8 b1, u8 b2, u8 b3, u8 b4)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0;
|
|
|
|
|
|
|
|
/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n",
|
|
|
|
b1, b2, b3, b4, val); */
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VC_LONG_PACKET_PAYLOAD(vc), val);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static int dsi_vc_send_long(struct dsi_data *dsi, int vc,
|
2020-12-15 12:45:44 +02:00
|
|
|
const struct mipi_dsi_msg *msg)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
/*u32 val; */
|
|
|
|
int i;
|
2020-12-15 12:45:37 +02:00
|
|
|
const u8 *p;
|
2009-10-28 11:59:56 +02:00
|
|
|
int r = 0;
|
|
|
|
u8 b1, b2, b3, b4;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_write)
|
2020-12-15 12:45:44 +02:00
|
|
|
DSSDBG("dsi_vc_send_long, %d bytes\n", msg->tx_len);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* len + header */
|
2020-12-15 12:46:40 +02:00
|
|
|
if (dsi->vc[vc].tx_fifo_size * 32 * 4 < msg->tx_len + 4) {
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSERR("unable to send long packet: packet too long.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
dsi_vc_write_long_header(dsi, vc, msg->channel, msg->type, msg->tx_len, 0);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:45:44 +02:00
|
|
|
p = msg->tx_buf;
|
|
|
|
for (i = 0; i < msg->tx_len >> 2; i++) {
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_write)
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSDBG("\tsending full packet %d\n", i);
|
|
|
|
|
|
|
|
b1 = *p++;
|
|
|
|
b2 = *p++;
|
|
|
|
b3 = *p++;
|
|
|
|
b4 = *p++;
|
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
dsi_vc_write_long_payload(dsi, vc, b1, b2, b3, b4);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:44 +02:00
|
|
|
i = msg->tx_len % 4;
|
2009-10-28 11:59:56 +02:00
|
|
|
if (i) {
|
|
|
|
b1 = 0; b2 = 0; b3 = 0;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_write)
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSDBG("\tsending remainder bytes %d\n", i);
|
|
|
|
|
|
|
|
switch (i) {
|
|
|
|
case 3:
|
|
|
|
b1 = *p++;
|
|
|
|
b2 = *p++;
|
|
|
|
b3 = *p++;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
b1 = *p++;
|
|
|
|
b2 = *p++;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
b1 = *p++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
dsi_vc_write_long_payload(dsi, vc, b1, b2, b3, 0);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static int dsi_vc_send_short(struct dsi_data *dsi, int vc,
|
2020-12-15 12:45:44 +02:00
|
|
|
const struct mipi_dsi_msg *msg)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2020-12-15 12:45:44 +02:00
|
|
|
struct mipi_dsi_packet pkt;
|
2021-03-12 15:14:45 +08:00
|
|
|
int ret;
|
2009-10-28 11:59:56 +02:00
|
|
|
u32 r;
|
2020-12-15 12:45:44 +02:00
|
|
|
|
2021-03-12 15:14:45 +08:00
|
|
|
ret = mipi_dsi_create_packet(&pkt, msg);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsi));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_write)
|
2020-12-15 12:46:40 +02:00
|
|
|
DSSDBG("dsi_vc_send_short(vc%d, dt %#x, b1 %#x, b2 %#x)\n",
|
|
|
|
vc, msg->type, pkt.header[1], pkt.header[2]);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
if (FLD_GET(dsi_read_reg(dsi, DSI_VC_CTRL(vc)), 16, 16)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSERR("ERROR FIFO FULL, aborting transfer\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:44 +02:00
|
|
|
r = pkt.header[3] << 24 | pkt.header[2] << 16 | pkt.header[1] << 8 |
|
|
|
|
pkt.header[0];
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc), r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2020-12-15 12:45:44 +02:00
|
|
|
const struct mipi_dsi_msg msg = {
|
2020-12-15 12:46:40 +02:00
|
|
|
.channel = channel,
|
2020-12-15 12:45:44 +02:00
|
|
|
.type = MIPI_DSI_NULL_PACKET,
|
|
|
|
};
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
return dsi_vc_send_long(dsi, vc, &msg);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static int dsi_vc_write_common(struct omap_dss_device *dssdev, int vc,
|
2020-12-15 12:45:42 +02:00
|
|
|
const struct mipi_dsi_msg *msg)
|
2011-08-25 18:35:58 +05:30
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
2020-12-15 12:45:42 +02:00
|
|
|
int r;
|
2012-08-14 12:29:22 +05:30
|
|
|
|
2020-12-15 12:45:44 +02:00
|
|
|
if (mipi_dsi_packet_format_is_short(msg->type))
|
2020-12-15 12:46:39 +02:00
|
|
|
r = dsi_vc_send_short(dsi, vc, msg);
|
2020-12-15 12:45:44 +02:00
|
|
|
else
|
2020-12-15 12:46:39 +02:00
|
|
|
r = dsi_vc_send_long(dsi, vc, msg);
|
2011-08-25 18:35:58 +05:30
|
|
|
|
2020-12-15 12:45:42 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:45:42 +02:00
|
|
|
/*
|
|
|
|
* TODO: we do not always have to do the BTA sync, for example
|
|
|
|
* we can improve performance by setting the update window
|
|
|
|
* information without sending BTA sync between the commands.
|
|
|
|
* In that case we can return early.
|
|
|
|
*/
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
r = dsi_vc_send_bta_sync(dssdev, vc);
|
2020-12-15 12:45:42 +02:00
|
|
|
if (r) {
|
|
|
|
DSSERR("bta sync failed\n");
|
|
|
|
return r;
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:26 +05:30
|
|
|
/* RX_FIFO_NOT_EMPTY */
|
2020-12-15 12:46:40 +02:00
|
|
|
if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) {
|
2010-04-09 13:20:57 +03:00
|
|
|
DSSERR("rx fifo not empty after write, dumping data:\n");
|
2020-12-15 12:46:40 +02:00
|
|
|
dsi_vc_flush_receive_data(dsi, vc);
|
2020-12-15 12:45:42 +02:00
|
|
|
return -EIO;
|
2010-04-09 13:20:57 +03:00
|
|
|
}
|
|
|
|
|
2010-02-26 11:32:56 +02:00
|
|
|
return 0;
|
2011-08-25 18:35:58 +05:30
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_vc_read_rx_fifo(struct dsi_data *dsi, int vc, u8 *buf,
|
2018-02-13 14:00:33 +02:00
|
|
|
int buflen, enum dss_dsi_content_type type)
|
2011-08-30 15:48:23 +05:30
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
u8 dt;
|
|
|
|
int r;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* RX_FIFO_NOT_EMPTY */
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20) == 0) {
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSERR("RX fifo empty when trying to read.\n");
|
2010-02-26 11:32:56 +02:00
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc));
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_read)
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSDBG("\theader: %08x\n", val);
|
|
|
|
dt = FLD_GET(val, 5, 0);
|
2011-08-25 18:25:03 +05:30
|
|
|
if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
|
2009-10-28 11:59:56 +02:00
|
|
|
u16 err = FLD_GET(val, 23, 8);
|
|
|
|
dsi_show_rx_ack_with_err(err);
|
2010-02-26 11:32:56 +02:00
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-08-30 16:07:39 +05:30
|
|
|
} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
|
|
|
|
MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE :
|
|
|
|
MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
u8 data = FLD_GET(val, 15, 8);
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_read)
|
2011-08-30 16:07:39 +05:30
|
|
|
DSSDBG("\t%s short response, 1 byte: %02x\n",
|
|
|
|
type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
|
|
|
|
"DCS", data);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-02-26 11:32:56 +02:00
|
|
|
if (buflen < 1) {
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
buf[0] = data;
|
|
|
|
|
|
|
|
return 1;
|
2011-08-30 16:07:39 +05:30
|
|
|
} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
|
|
|
|
MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE :
|
|
|
|
MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
u16 data = FLD_GET(val, 23, 8);
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_read)
|
2011-08-30 16:07:39 +05:30
|
|
|
DSSDBG("\t%s short response, 2 byte: %04x\n",
|
|
|
|
type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
|
|
|
|
"DCS", data);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-02-26 11:32:56 +02:00
|
|
|
if (buflen < 2) {
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
buf[0] = data & 0xff;
|
|
|
|
buf[1] = (data >> 8) & 0xff;
|
|
|
|
|
|
|
|
return 2;
|
2011-08-30 16:07:39 +05:30
|
|
|
} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
|
|
|
|
MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE :
|
|
|
|
MIPI_DSI_RX_DCS_LONG_READ_RESPONSE)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
int w;
|
|
|
|
int len = FLD_GET(val, 23, 8);
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_read)
|
2011-08-30 16:07:39 +05:30
|
|
|
DSSDBG("\t%s long response, len %d\n",
|
|
|
|
type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
|
|
|
|
"DCS", len);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-02-26 11:32:56 +02:00
|
|
|
if (len > buflen) {
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* two byte checksum ends the packet, not included in len */
|
|
|
|
for (w = 0; w < len + 2;) {
|
|
|
|
int b;
|
2018-02-13 14:00:33 +02:00
|
|
|
val = dsi_read_reg(dsi,
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
DSI_VC_SHORT_PACKET_HEADER(vc));
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->debug_read)
|
2009-10-28 11:59:56 +02:00
|
|
|
DSSDBG("\t\t%02x %02x %02x %02x\n",
|
|
|
|
(val >> 0) & 0xff,
|
|
|
|
(val >> 8) & 0xff,
|
|
|
|
(val >> 16) & 0xff,
|
|
|
|
(val >> 24) & 0xff);
|
|
|
|
|
|
|
|
for (b = 0; b < 4; ++b) {
|
|
|
|
if (w < len)
|
|
|
|
buf[w] = (val >> (b * 8)) & 0xff;
|
|
|
|
/* we discard the 2 byte checksum */
|
|
|
|
++w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
} else {
|
|
|
|
DSSERR("\tunknown datatype 0x%02x\n", dt);
|
2010-02-26 11:32:56 +02:00
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
2010-02-26 11:32:56 +02:00
|
|
|
|
|
|
|
err:
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
DSSERR("dsi_vc_read_rx_fifo(vc %d type %s) failed\n", vc,
|
2011-08-30 16:07:39 +05:30
|
|
|
type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS");
|
2011-08-30 15:48:23 +05:30
|
|
|
|
2010-02-26 11:32:56 +02:00
|
|
|
return r;
|
2011-08-30 15:48:23 +05:30
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int vc,
|
2020-12-15 12:45:43 +02:00
|
|
|
const struct mipi_dsi_msg *msg)
|
2011-08-30 15:48:23 +05:30
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
2020-12-15 12:45:44 +02:00
|
|
|
u8 cmd = ((u8 *)msg->tx_buf)[0];
|
2011-08-30 15:48:23 +05:30
|
|
|
int r;
|
|
|
|
|
2020-12-15 12:45:43 +02:00
|
|
|
if (dsi->debug_read)
|
2020-12-15 12:46:40 +02:00
|
|
|
DSSDBG("%s(vc %d, cmd %x)\n", __func__, vc, cmd);
|
2020-12-15 12:45:43 +02:00
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
r = dsi_vc_send_short(dsi, vc, msg);
|
2011-08-30 15:48:23 +05:30
|
|
|
if (r)
|
|
|
|
goto err;
|
2010-02-26 11:32:56 +02:00
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
r = dsi_vc_send_bta_sync(dssdev, vc);
|
2011-08-30 15:48:23 +05:30
|
|
|
if (r)
|
|
|
|
goto err;
|
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
r = dsi_vc_read_rx_fifo(dsi, vc, msg->rx_buf, msg->rx_len,
|
2011-08-30 16:07:39 +05:30
|
|
|
DSS_DSI_CONTENT_DCS);
|
2011-08-30 15:48:23 +05:30
|
|
|
if (r < 0)
|
|
|
|
goto err;
|
|
|
|
|
2020-12-15 12:45:43 +02:00
|
|
|
if (r != msg->rx_len) {
|
2011-08-30 15:48:23 +05:30
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
err:
|
2020-12-15 12:46:40 +02:00
|
|
|
DSSERR("%s(vc %d, cmd 0x%02x) failed\n", __func__, vc, cmd);
|
2011-08-30 15:48:23 +05:30
|
|
|
return r;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int vc,
|
2020-12-15 12:45:43 +02:00
|
|
|
const struct mipi_dsi_msg *msg)
|
2011-08-30 16:07:39 +05:30
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
2011-08-30 16:07:39 +05:30
|
|
|
int r;
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
r = dsi_vc_send_short(dsi, vc, msg);
|
2011-08-30 16:07:39 +05:30
|
|
|
if (r)
|
2020-12-15 12:45:43 +02:00
|
|
|
goto err;
|
2011-08-30 16:07:39 +05:30
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
r = dsi_vc_send_bta_sync(dssdev, vc);
|
2011-08-30 16:07:39 +05:30
|
|
|
if (r)
|
2020-12-15 12:45:43 +02:00
|
|
|
goto err;
|
2011-08-30 16:07:39 +05:30
|
|
|
|
2020-12-15 12:46:40 +02:00
|
|
|
r = dsi_vc_read_rx_fifo(dsi, vc, msg->rx_buf, msg->rx_len,
|
2011-08-30 16:07:39 +05:30
|
|
|
DSS_DSI_CONTENT_GENERIC);
|
|
|
|
if (r < 0)
|
2020-12-15 12:45:43 +02:00
|
|
|
goto err;
|
2011-08-30 16:07:39 +05:30
|
|
|
|
2020-12-15 12:45:43 +02:00
|
|
|
if (r != msg->rx_len) {
|
2011-08-30 16:07:39 +05:30
|
|
|
r = -EIO;
|
2020-12-15 12:45:43 +02:00
|
|
|
goto err;
|
2011-08-30 16:07:39 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-12-15 12:45:43 +02:00
|
|
|
err:
|
2020-12-15 12:46:40 +02:00
|
|
|
DSSERR("%s(vc %d, reqlen %d) failed\n", __func__, vc, msg->tx_len);
|
2020-12-15 12:45:43 +02:00
|
|
|
return r;
|
2011-08-30 16:07:39 +05:30
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_set_lp_rx_timeout(struct dsi_data *dsi, unsigned int ticks,
|
|
|
|
bool x4, bool x16)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
unsigned long fck;
|
2010-04-12 10:40:12 +03:00
|
|
|
unsigned long total_ticks;
|
|
|
|
u32 r;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
BUG_ON(ticks > 0x1fff);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
/* ticks in DSI_FCK */
|
2018-02-13 14:00:33 +02:00
|
|
|
fck = dsi_fclk_rate(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_TIMING2);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */
|
2010-04-12 10:40:12 +03:00
|
|
|
r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */
|
|
|
|
r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_TIMING2, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
|
|
|
|
|
|
|
|
DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n",
|
|
|
|
total_ticks,
|
|
|
|
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
|
|
|
|
(total_ticks * 1000) / (fck / 1000 / 1000));
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_set_ta_timeout(struct dsi_data *dsi, unsigned int ticks,
|
|
|
|
bool x8, bool x16)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
unsigned long fck;
|
2010-04-12 10:40:12 +03:00
|
|
|
unsigned long total_ticks;
|
|
|
|
u32 r;
|
|
|
|
|
|
|
|
BUG_ON(ticks > 0x1fff);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* ticks in DSI_FCK */
|
2018-02-13 14:00:33 +02:00
|
|
|
fck = dsi_fclk_rate(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_TIMING1);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, 1, 31, 31); /* TA_TO */
|
2010-04-12 10:40:12 +03:00
|
|
|
r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */
|
|
|
|
r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_TIMING1, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1);
|
|
|
|
|
|
|
|
DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n",
|
|
|
|
total_ticks,
|
|
|
|
ticks, x8 ? " x8" : "", x16 ? " x16" : "",
|
|
|
|
(total_ticks * 1000) / (fck / 1000 / 1000));
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_set_stop_state_counter(struct dsi_data *dsi, unsigned int ticks,
|
|
|
|
bool x4, bool x16)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
unsigned long fck;
|
2010-04-12 10:40:12 +03:00
|
|
|
unsigned long total_ticks;
|
|
|
|
u32 r;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
BUG_ON(ticks > 0x1fff);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
/* ticks in DSI_FCK */
|
2018-02-13 14:00:33 +02:00
|
|
|
fck = dsi_fclk_rate(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_TIMING1);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
|
2010-04-12 10:40:12 +03:00
|
|
|
r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */
|
|
|
|
r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_TIMING1, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
|
|
|
|
|
|
|
|
DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n",
|
|
|
|
total_ticks,
|
|
|
|
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
|
|
|
|
(total_ticks * 1000) / (fck / 1000 / 1000));
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_set_hs_tx_timeout(struct dsi_data *dsi, unsigned int ticks,
|
|
|
|
bool x4, bool x16)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
unsigned long fck;
|
2010-04-12 10:40:12 +03:00
|
|
|
unsigned long total_ticks;
|
|
|
|
u32 r;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
BUG_ON(ticks > 0x1fff);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
/* ticks in TxByteClkHS */
|
2018-02-13 14:00:33 +02:00
|
|
|
fck = dsi_get_txbyteclkhs(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_TIMING2);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */
|
2010-04-12 10:40:12 +03:00
|
|
|
r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */
|
|
|
|
r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_TIMING2, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-04-12 10:40:12 +03:00
|
|
|
total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
|
|
|
|
|
|
|
|
DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n",
|
|
|
|
total_ticks,
|
|
|
|
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
|
|
|
|
(total_ticks * 1000) / (fck / 1000 / 1000));
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_config_vp_num_line_buffers(struct dsi_data *dsi)
|
2011-09-05 16:48:27 +05:30
|
|
|
{
|
|
|
|
int num_line_buffers;
|
|
|
|
|
2012-08-16 18:02:00 +05:30
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2020-12-15 12:45:36 +02:00
|
|
|
int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
|
2018-06-08 15:59:31 +03:00
|
|
|
const struct videomode *vm = &dsi->vm;
|
2011-09-05 16:48:27 +05:30
|
|
|
/*
|
|
|
|
* Don't use line buffers if width is greater than the video
|
|
|
|
* port's line buffer size
|
|
|
|
*/
|
2016-09-22 14:07:04 +03:00
|
|
|
if (dsi->line_buffer_size <= vm->hactive * bpp / 8)
|
2011-09-05 16:48:27 +05:30
|
|
|
num_line_buffers = 0;
|
|
|
|
else
|
|
|
|
num_line_buffers = 2;
|
|
|
|
} else {
|
|
|
|
/* Use maximum number of line buffers in command mode */
|
|
|
|
num_line_buffers = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* LINE_BUFFER */
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_CTRL, num_line_buffers, 13, 12);
|
2011-09-05 16:48:27 +05:30
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_config_vp_sync_events(struct dsi_data *dsi)
|
2011-09-05 16:48:27 +05:30
|
|
|
{
|
2013-03-05 16:29:36 +02:00
|
|
|
bool sync_end;
|
2011-09-05 16:48:27 +05:30
|
|
|
u32 r;
|
|
|
|
|
2013-03-05 16:29:36 +02:00
|
|
|
if (dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE)
|
|
|
|
sync_end = true;
|
|
|
|
else
|
|
|
|
sync_end = false;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_CTRL);
|
2012-06-26 12:38:31 +05:30
|
|
|
r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */
|
|
|
|
r = FLD_MOD(r, 1, 10, 10); /* VP_HSYNC_POL */
|
|
|
|
r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */
|
2011-09-05 16:48:27 +05:30
|
|
|
r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */
|
2013-03-05 16:29:36 +02:00
|
|
|
r = FLD_MOD(r, sync_end, 16, 16); /* VP_VSYNC_END */
|
2011-09-05 16:48:27 +05:30
|
|
|
r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */
|
2013-03-05 16:29:36 +02:00
|
|
|
r = FLD_MOD(r, sync_end, 18, 18); /* VP_HSYNC_END */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_CTRL, r);
|
2011-09-05 16:48:27 +05:30
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_config_blanking_modes(struct dsi_data *dsi)
|
2011-09-05 16:48:27 +05:30
|
|
|
{
|
2012-08-13 22:13:39 +05:30
|
|
|
int blanking_mode = dsi->vm_timings.blanking_mode;
|
|
|
|
int hfp_blanking_mode = dsi->vm_timings.hfp_blanking_mode;
|
|
|
|
int hbp_blanking_mode = dsi->vm_timings.hbp_blanking_mode;
|
|
|
|
int hsa_blanking_mode = dsi->vm_timings.hsa_blanking_mode;
|
2011-09-05 16:48:27 +05:30
|
|
|
u32 r;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 0 = TX FIFO packets sent or LPS in corresponding blanking periods
|
|
|
|
* 1 = Long blanking packets are sent in corresponding blanking periods
|
|
|
|
*/
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_CTRL);
|
2011-09-05 16:48:27 +05:30
|
|
|
r = FLD_MOD(r, blanking_mode, 20, 20); /* BLANKING_MODE */
|
|
|
|
r = FLD_MOD(r, hfp_blanking_mode, 21, 21); /* HFP_BLANKING */
|
|
|
|
r = FLD_MOD(r, hbp_blanking_mode, 22, 22); /* HBP_BLANKING */
|
|
|
|
r = FLD_MOD(r, hsa_blanking_mode, 23, 23); /* HSA_BLANKING */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_CTRL, r);
|
2011-09-05 16:48:27 +05:30
|
|
|
}
|
|
|
|
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
/*
|
|
|
|
* According to section 'HS Command Mode Interleaving' in OMAP TRM, Scenario 3
|
|
|
|
* results in maximum transition time for data and clock lanes to enter and
|
|
|
|
* exit HS mode. Hence, this is the scenario where the least amount of command
|
|
|
|
* mode data can be interleaved. We program the minimum amount of TXBYTECLKHS
|
|
|
|
* clock cycles that can be used to interleave command mode data in HS so that
|
|
|
|
* all scenarios are satisfied.
|
|
|
|
*/
|
|
|
|
static int dsi_compute_interleave_hs(int blank, bool ddr_alwon, int enter_hs,
|
|
|
|
int exit_hs, int exiths_clk, int ddr_pre, int ddr_post)
|
|
|
|
{
|
|
|
|
int transition;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If DDR_CLK_ALWAYS_ON is set, we need to consider HS mode transition
|
|
|
|
* time of data lanes only, if it isn't set, we need to consider HS
|
|
|
|
* transition time of both data and clock lanes. HS transition time
|
|
|
|
* of Scenario 3 is considered.
|
|
|
|
*/
|
|
|
|
if (ddr_alwon) {
|
|
|
|
transition = enter_hs + exit_hs + max(enter_hs, 2) + 1;
|
|
|
|
} else {
|
|
|
|
int trans1, trans2;
|
|
|
|
trans1 = ddr_pre + enter_hs + exit_hs + max(enter_hs, 2) + 1;
|
|
|
|
trans2 = ddr_pre + enter_hs + exiths_clk + ddr_post + ddr_pre +
|
|
|
|
enter_hs + 1;
|
|
|
|
transition = max(trans1, trans2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return blank > transition ? blank - transition : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* According to section 'LP Command Mode Interleaving' in OMAP TRM, Scenario 1
|
|
|
|
* results in maximum transition time for data lanes to enter and exit LP mode.
|
|
|
|
* Hence, this is the scenario where the least amount of command mode data can
|
|
|
|
* be interleaved. We program the minimum amount of bytes that can be
|
|
|
|
* interleaved in LP so that all scenarios are satisfied.
|
|
|
|
*/
|
|
|
|
static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs,
|
|
|
|
int lp_clk_div, int tdsi_fclk)
|
|
|
|
{
|
|
|
|
int trans_lp; /* time required for a LP transition, in TXBYTECLKHS */
|
|
|
|
int tlp_avail; /* time left for interleaving commands, in CLKIN4DDR */
|
|
|
|
int ttxclkesc; /* period of LP transmit escape clock, in CLKIN4DDR */
|
|
|
|
int thsbyte_clk = 16; /* Period of TXBYTECLKHS clock, in CLKIN4DDR */
|
|
|
|
int lp_inter; /* cmd mode data that can be interleaved, in bytes */
|
|
|
|
|
|
|
|
/* maximum LP transition time according to Scenario 1 */
|
|
|
|
trans_lp = exit_hs + max(enter_hs, 2) + 1;
|
|
|
|
|
|
|
|
/* CLKIN4DDR = 16 * TXBYTECLKHS */
|
|
|
|
tlp_avail = thsbyte_clk * (blank - trans_lp);
|
|
|
|
|
2012-06-04 13:36:34 +05:30
|
|
|
ttxclkesc = tdsi_fclk * lp_clk_div;
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
|
|
|
|
lp_inter = ((tlp_avail - 8 * thsbyte_clk - 5 * tdsi_fclk) / ttxclkesc -
|
|
|
|
26) / 16;
|
|
|
|
|
|
|
|
return max(lp_inter, 0);
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_config_cmd_mode_interleaving(struct dsi_data *dsi)
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
{
|
|
|
|
int blanking_mode;
|
|
|
|
int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode;
|
|
|
|
int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div;
|
|
|
|
int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat;
|
|
|
|
int tclk_trail, ths_exit, exiths_clk;
|
|
|
|
bool ddr_alwon;
|
2018-06-08 15:59:31 +03:00
|
|
|
const struct videomode *vm = &dsi->vm;
|
2020-12-15 12:45:36 +02:00
|
|
|
int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
int ndl = dsi->num_lanes_used - 1;
|
2014-10-22 14:49:14 +03:00
|
|
|
int dsi_fclk_hsdiv = dsi->user_dsi_cinfo.mX[HSDIV_DSI] + 1;
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
int hsa_interleave_hs = 0, hsa_interleave_lp = 0;
|
|
|
|
int hfp_interleave_hs = 0, hfp_interleave_lp = 0;
|
|
|
|
int hbp_interleave_hs = 0, hbp_interleave_lp = 0;
|
|
|
|
int bl_interleave_hs = 0, bl_interleave_lp = 0;
|
|
|
|
u32 r;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_CTRL);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
blanking_mode = FLD_GET(r, 20, 20);
|
|
|
|
hfp_blanking_mode = FLD_GET(r, 21, 21);
|
|
|
|
hbp_blanking_mode = FLD_GET(r, 22, 22);
|
|
|
|
hsa_blanking_mode = FLD_GET(r, 23, 23);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_VM_TIMING1);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
hbp = FLD_GET(r, 11, 0);
|
|
|
|
hfp = FLD_GET(r, 23, 12);
|
|
|
|
hsa = FLD_GET(r, 31, 24);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_CLK_TIMING);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
ddr_clk_post = FLD_GET(r, 7, 0);
|
|
|
|
ddr_clk_pre = FLD_GET(r, 15, 8);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_VM_TIMING7);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
exit_hs_mode_lat = FLD_GET(r, 15, 0);
|
|
|
|
enter_hs_mode_lat = FLD_GET(r, 31, 16);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_CLK_CTRL);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
lp_clk_div = FLD_GET(r, 12, 0);
|
|
|
|
ddr_alwon = FLD_GET(r, 13, 13);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG0);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
ths_exit = FLD_GET(r, 7, 0);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
tclk_trail = FLD_GET(r, 15, 8);
|
|
|
|
|
|
|
|
exiths_clk = ths_exit + tclk_trail;
|
|
|
|
|
2016-09-22 14:07:04 +03:00
|
|
|
width_bytes = DIV_ROUND_UP(vm->hactive * bpp, 8);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
bllp = hbp + hfp + hsa + DIV_ROUND_UP(width_bytes + 6, ndl);
|
|
|
|
|
|
|
|
if (!hsa_blanking_mode) {
|
|
|
|
hsa_interleave_hs = dsi_compute_interleave_hs(hsa, ddr_alwon,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
exiths_clk, ddr_clk_pre, ddr_clk_post);
|
|
|
|
hsa_interleave_lp = dsi_compute_interleave_lp(hsa,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
lp_clk_div, dsi_fclk_hsdiv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hfp_blanking_mode) {
|
|
|
|
hfp_interleave_hs = dsi_compute_interleave_hs(hfp, ddr_alwon,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
exiths_clk, ddr_clk_pre, ddr_clk_post);
|
|
|
|
hfp_interleave_lp = dsi_compute_interleave_lp(hfp,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
lp_clk_div, dsi_fclk_hsdiv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hbp_blanking_mode) {
|
|
|
|
hbp_interleave_hs = dsi_compute_interleave_hs(hbp, ddr_alwon,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
exiths_clk, ddr_clk_pre, ddr_clk_post);
|
|
|
|
|
|
|
|
hbp_interleave_lp = dsi_compute_interleave_lp(hbp,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
lp_clk_div, dsi_fclk_hsdiv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!blanking_mode) {
|
|
|
|
bl_interleave_hs = dsi_compute_interleave_hs(bllp, ddr_alwon,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
exiths_clk, ddr_clk_pre, ddr_clk_post);
|
|
|
|
|
|
|
|
bl_interleave_lp = dsi_compute_interleave_lp(bllp,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
lp_clk_div, dsi_fclk_hsdiv);
|
|
|
|
}
|
|
|
|
|
|
|
|
DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n",
|
|
|
|
hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs,
|
|
|
|
bl_interleave_hs);
|
|
|
|
|
|
|
|
DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n",
|
|
|
|
hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp,
|
|
|
|
bl_interleave_lp);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_VM_TIMING4);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
r = FLD_MOD(r, hsa_interleave_hs, 23, 16);
|
|
|
|
r = FLD_MOD(r, hfp_interleave_hs, 15, 8);
|
|
|
|
r = FLD_MOD(r, hbp_interleave_hs, 7, 0);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VM_TIMING4, r);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_VM_TIMING5);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
r = FLD_MOD(r, hsa_interleave_lp, 23, 16);
|
|
|
|
r = FLD_MOD(r, hfp_interleave_lp, 15, 8);
|
|
|
|
r = FLD_MOD(r, hbp_interleave_lp, 7, 0);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VM_TIMING5, r);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_VM_TIMING6);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
r = FLD_MOD(r, bl_interleave_hs, 31, 15);
|
|
|
|
r = FLD_MOD(r, bl_interleave_lp, 16, 0);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VM_TIMING6, r);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 11:32:18 +05:30
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_proto_config(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
u32 r;
|
|
|
|
int buswidth = 0;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_config_tx_fifo(dsi, DSI_FIFO_SIZE_32,
|
2009-12-16 16:49:03 +02:00
|
|
|
DSI_FIFO_SIZE_32,
|
|
|
|
DSI_FIFO_SIZE_32,
|
|
|
|
DSI_FIFO_SIZE_32);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_config_rx_fifo(dsi, DSI_FIFO_SIZE_32,
|
2009-12-16 16:49:03 +02:00
|
|
|
DSI_FIFO_SIZE_32,
|
|
|
|
DSI_FIFO_SIZE_32,
|
|
|
|
DSI_FIFO_SIZE_32);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* XXX what values for the timeouts? */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_set_stop_state_counter(dsi, 0x1000, false, false);
|
|
|
|
dsi_set_ta_timeout(dsi, 0x1fff, true, true);
|
|
|
|
dsi_set_lp_rx_timeout(dsi, 0x1fff, true, true);
|
|
|
|
dsi_set_hs_tx_timeout(dsi, 0x1fff, true, true);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:45:36 +02:00
|
|
|
switch (mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt)) {
|
2009-10-28 11:59:56 +02:00
|
|
|
case 16:
|
|
|
|
buswidth = 0;
|
|
|
|
break;
|
|
|
|
case 18:
|
|
|
|
buswidth = 1;
|
|
|
|
break;
|
|
|
|
case 24:
|
|
|
|
buswidth = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
2012-05-18 11:47:02 +03:00
|
|
|
return -EINVAL;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_CTRL);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */
|
|
|
|
r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */
|
|
|
|
r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */
|
|
|
|
r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/
|
|
|
|
r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
|
|
|
|
r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */
|
|
|
|
r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */
|
|
|
|
r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */
|
2017-08-05 01:44:10 +03:00
|
|
|
if (!(dsi->data->quirks & DSI_QUIRK_DCS_CMD_CONFIG_VC)) {
|
2011-03-22 06:33:36 -05:00
|
|
|
r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */
|
|
|
|
/* DCS_CMD_CODE, 1=start, 0=continue */
|
|
|
|
r = FLD_MOD(r, 0, 25, 25);
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_CTRL, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_config_vp_num_line_buffers(dsi);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2012-08-16 18:02:00 +05:30
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_config_vp_sync_events(dsi);
|
|
|
|
dsi_config_blanking_modes(dsi);
|
|
|
|
dsi_config_cmd_mode_interleaving(dsi);
|
2011-09-05 16:48:27 +05:30
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_vc_initial_config(dsi, 0);
|
|
|
|
dsi_vc_initial_config(dsi, 1);
|
|
|
|
dsi_vc_initial_config(dsi, 2);
|
|
|
|
dsi_vc_initial_config(dsi, 3);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_proto_timings(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2019-10-08 15:15:47 +08:00
|
|
|
unsigned int tlpx, tclk_zero, tclk_prepare;
|
2018-02-11 15:07:34 +02:00
|
|
|
unsigned int tclk_pre, tclk_post;
|
|
|
|
unsigned int ths_prepare, ths_prepare_ths_zero, ths_zero;
|
|
|
|
unsigned int ths_trail, ths_exit;
|
|
|
|
unsigned int ddr_clk_pre, ddr_clk_post;
|
|
|
|
unsigned int enter_hs_mode_lat, exit_hs_mode_lat;
|
|
|
|
unsigned int ths_eot;
|
2011-10-13 16:12:29 +03:00
|
|
|
int ndl = dsi->num_lanes_used - 1;
|
2009-10-28 11:59:56 +02:00
|
|
|
u32 r;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG0);
|
2009-10-28 11:59:56 +02:00
|
|
|
ths_prepare = FLD_GET(r, 31, 24);
|
|
|
|
ths_prepare_ths_zero = FLD_GET(r, 23, 16);
|
|
|
|
ths_zero = ths_prepare_ths_zero - ths_prepare;
|
|
|
|
ths_trail = FLD_GET(r, 15, 8);
|
|
|
|
ths_exit = FLD_GET(r, 7, 0);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1);
|
2012-09-24 09:34:52 +03:00
|
|
|
tlpx = FLD_GET(r, 20, 16) * 2;
|
2009-10-28 11:59:56 +02:00
|
|
|
tclk_zero = FLD_GET(r, 7, 0);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG2);
|
2009-10-28 11:59:56 +02:00
|
|
|
tclk_prepare = FLD_GET(r, 7, 0);
|
|
|
|
|
|
|
|
/* min 8*UI */
|
|
|
|
tclk_pre = 20;
|
|
|
|
/* min 60ns + 52*UI */
|
2018-02-13 14:00:33 +02:00
|
|
|
tclk_post = ns2ddr(dsi, 60) + 26;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-09-05 16:48:27 +05:30
|
|
|
ths_eot = DIV_ROUND_UP(4, ndl);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
|
|
|
|
4);
|
|
|
|
ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot;
|
|
|
|
|
|
|
|
BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255);
|
|
|
|
BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255);
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_CLK_TIMING);
|
2009-10-28 11:59:56 +02:00
|
|
|
r = FLD_MOD(r, ddr_clk_pre, 15, 8);
|
|
|
|
r = FLD_MOD(r, ddr_clk_post, 7, 0);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_CLK_TIMING, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n",
|
|
|
|
ddr_clk_pre,
|
|
|
|
ddr_clk_post);
|
|
|
|
|
|
|
|
enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) +
|
|
|
|
DIV_ROUND_UP(ths_prepare, 4) +
|
|
|
|
DIV_ROUND_UP(ths_zero + 3, 4);
|
|
|
|
|
|
|
|
exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot;
|
|
|
|
|
|
|
|
r = FLD_VAL(enter_hs_mode_lat, 31, 16) |
|
|
|
|
FLD_VAL(exit_hs_mode_lat, 15, 0);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VM_TIMING7, r);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n",
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2012-08-16 18:02:00 +05:30
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2011-09-05 16:48:27 +05:30
|
|
|
/* TODO: Implement a video mode check_timings function */
|
2012-08-13 22:13:39 +05:30
|
|
|
int hsa = dsi->vm_timings.hsa;
|
|
|
|
int hfp = dsi->vm_timings.hfp;
|
|
|
|
int hbp = dsi->vm_timings.hbp;
|
|
|
|
int vsa = dsi->vm_timings.vsa;
|
|
|
|
int vfp = dsi->vm_timings.vfp;
|
|
|
|
int vbp = dsi->vm_timings.vbp;
|
|
|
|
int window_sync = dsi->vm_timings.window_sync;
|
2013-03-05 16:29:36 +02:00
|
|
|
bool hsync_end;
|
2018-06-08 15:59:31 +03:00
|
|
|
const struct videomode *vm = &dsi->vm;
|
2020-12-15 12:45:36 +02:00
|
|
|
int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
|
2011-09-05 16:48:27 +05:30
|
|
|
int tl, t_he, width_bytes;
|
|
|
|
|
2013-03-05 16:29:36 +02:00
|
|
|
hsync_end = dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE;
|
2011-09-05 16:48:27 +05:30
|
|
|
t_he = hsync_end ?
|
|
|
|
((hsa == 0 && ndl == 3) ? 1 : DIV_ROUND_UP(4, ndl)) : 0;
|
|
|
|
|
2016-09-22 14:07:04 +03:00
|
|
|
width_bytes = DIV_ROUND_UP(vm->hactive * bpp, 8);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
|
|
|
/* TL = t_HS + HSA + t_HE + HFP + ceil((WC + 6) / NDL) + HBP */
|
|
|
|
tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp +
|
|
|
|
DIV_ROUND_UP(width_bytes + 6, ndl) + hbp;
|
|
|
|
|
|
|
|
DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp,
|
|
|
|
hfp, hsync_end ? hsa : 0, tl);
|
|
|
|
DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp,
|
2016-09-22 14:07:04 +03:00
|
|
|
vsa, vm->vactive);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_VM_TIMING1);
|
2011-09-05 16:48:27 +05:30
|
|
|
r = FLD_MOD(r, hbp, 11, 0); /* HBP */
|
|
|
|
r = FLD_MOD(r, hfp, 23, 12); /* HFP */
|
|
|
|
r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24); /* HSA */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VM_TIMING1, r);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_VM_TIMING2);
|
2011-09-05 16:48:27 +05:30
|
|
|
r = FLD_MOD(r, vbp, 7, 0); /* VBP */
|
|
|
|
r = FLD_MOD(r, vfp, 15, 8); /* VFP */
|
|
|
|
r = FLD_MOD(r, vsa, 23, 16); /* VSA */
|
|
|
|
r = FLD_MOD(r, window_sync, 27, 24); /* WINDOW_SYNC */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VM_TIMING2, r);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_read_reg(dsi, DSI_VM_TIMING3);
|
2016-09-22 14:07:04 +03:00
|
|
|
r = FLD_MOD(r, vm->vactive, 14, 0); /* VACT */
|
2011-09-05 16:48:27 +05:30
|
|
|
r = FLD_MOD(r, tl, 31, 16); /* TL */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VM_TIMING3, r);
|
2011-09-05 16:48:27 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:21 +02:00
|
|
|
static int dsi_configure_pins(struct dsi_data *dsi,
|
|
|
|
int num_pins, const u32 *pins)
|
2012-03-28 15:58:56 +03:00
|
|
|
{
|
|
|
|
struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
|
|
|
|
int num_lanes;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
static const enum dsi_lane_function functions[] = {
|
|
|
|
DSI_LANE_CLK,
|
|
|
|
DSI_LANE_DATA1,
|
|
|
|
DSI_LANE_DATA2,
|
|
|
|
DSI_LANE_DATA3,
|
|
|
|
DSI_LANE_DATA4,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (num_pins < 4 || num_pins > dsi->num_lanes_supported * 2
|
|
|
|
|| num_pins % 2 != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (i = 0; i < DSI_MAX_NR_LANES; ++i)
|
|
|
|
lanes[i].function = DSI_LANE_UNUSED;
|
|
|
|
|
|
|
|
num_lanes = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < num_pins; i += 2) {
|
|
|
|
u8 lane, pol;
|
2020-12-15 12:46:21 +02:00
|
|
|
u32 dx, dy;
|
2012-03-28 15:58:56 +03:00
|
|
|
|
|
|
|
dx = pins[i];
|
|
|
|
dy = pins[i + 1];
|
|
|
|
|
2020-12-15 12:46:21 +02:00
|
|
|
if (dx >= dsi->num_lanes_supported * 2)
|
2012-03-28 15:58:56 +03:00
|
|
|
return -EINVAL;
|
|
|
|
|
2020-12-15 12:46:21 +02:00
|
|
|
if (dy >= dsi->num_lanes_supported * 2)
|
2012-03-28 15:58:56 +03:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (dx & 1) {
|
|
|
|
if (dy != dx - 1)
|
|
|
|
return -EINVAL;
|
|
|
|
pol = 1;
|
|
|
|
} else {
|
|
|
|
if (dy != dx + 1)
|
|
|
|
return -EINVAL;
|
|
|
|
pol = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
lane = dx / 2;
|
|
|
|
|
|
|
|
lanes[lane].function = functions[i / 2];
|
|
|
|
lanes[lane].polarity = pol;
|
|
|
|
num_lanes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(dsi->lanes, lanes, sizeof(dsi->lanes));
|
|
|
|
dsi->num_lanes_used = num_lanes;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:53 +02:00
|
|
|
static int dsi_enable_video_mode(struct dsi_data *dsi, int vc)
|
2011-09-05 16:48:27 +05:30
|
|
|
{
|
2020-12-15 12:45:36 +02:00
|
|
|
int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
|
2011-09-05 16:48:27 +05:30
|
|
|
u8 data_type;
|
|
|
|
u16 word_count;
|
2020-12-15 12:46:53 +02:00
|
|
|
|
|
|
|
switch (dsi->pix_fmt) {
|
|
|
|
case MIPI_DSI_FMT_RGB888:
|
|
|
|
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
|
|
|
|
break;
|
|
|
|
case MIPI_DSI_FMT_RGB666:
|
|
|
|
data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
|
|
|
|
break;
|
|
|
|
case MIPI_DSI_FMT_RGB666_PACKED:
|
|
|
|
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
|
|
|
|
break;
|
|
|
|
case MIPI_DSI_FMT_RGB565:
|
|
|
|
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi_if_enable(dsi, false);
|
|
|
|
dsi_vc_enable(dsi, vc, false);
|
|
|
|
|
|
|
|
/* MODE, 1 = video mode */
|
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), 1, 4, 4);
|
|
|
|
|
|
|
|
word_count = DIV_ROUND_UP(dsi->vm.hactive * bpp, 8);
|
|
|
|
|
|
|
|
dsi_vc_write_long_header(dsi, vc, dsi->dsidev->channel, data_type,
|
|
|
|
word_count, 0);
|
|
|
|
|
|
|
|
dsi_vc_enable(dsi, vc, true);
|
|
|
|
dsi_if_enable(dsi, true);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_disable_video_mode(struct dsi_data *dsi, int vc)
|
|
|
|
{
|
|
|
|
dsi_if_enable(dsi, false);
|
|
|
|
dsi_vc_enable(dsi, vc, false);
|
|
|
|
|
|
|
|
/* MODE, 0 = command mode */
|
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), 0, 4, 4);
|
|
|
|
|
|
|
|
dsi_vc_enable(dsi, vc, true);
|
|
|
|
dsi_if_enable(dsi, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_enable_video_output(struct omap_dss_device *dssdev, int vc)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
2011-11-21 13:42:58 +02:00
|
|
|
int r;
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2020-12-15 12:46:51 +02:00
|
|
|
r = dsi_init_dispc(dsi);
|
2020-12-15 12:46:05 +02:00
|
|
|
if (r) {
|
|
|
|
dev_err(dsi->dev, "failed to init dispc!\n");
|
|
|
|
return;
|
|
|
|
}
|
2013-02-22 12:58:35 +02:00
|
|
|
|
2012-08-16 18:02:00 +05:30
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2020-12-15 12:46:53 +02:00
|
|
|
r = dsi_enable_video_mode(dsi, vc);
|
|
|
|
if (r)
|
|
|
|
goto err_video_mode;
|
2011-11-09 15:30:11 +02:00
|
|
|
}
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2018-02-13 14:00:38 +02:00
|
|
|
r = dss_mgr_enable(&dsi->output);
|
2013-02-22 12:58:35 +02:00
|
|
|
if (r)
|
|
|
|
goto err_mgr_enable;
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2020-12-15 12:46:05 +02:00
|
|
|
return;
|
2013-02-22 12:58:35 +02:00
|
|
|
|
|
|
|
err_mgr_enable:
|
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_if_enable(dsi, false);
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_vc_enable(dsi, vc, false);
|
2013-02-22 12:58:35 +02:00
|
|
|
}
|
2020-12-15 12:46:53 +02:00
|
|
|
err_video_mode:
|
2020-12-15 12:46:51 +02:00
|
|
|
dsi_uninit_dispc(dsi);
|
2020-12-15 12:46:05 +02:00
|
|
|
dev_err(dsi->dev, "failed to enable DSI encoder!\n");
|
|
|
|
return;
|
2011-09-05 16:48:27 +05:30
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static void dsi_disable_video_output(struct omap_dss_device *dssdev, int vc)
|
2011-09-05 16:48:27 +05:30
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2020-12-15 12:46:53 +02:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE)
|
|
|
|
dsi_disable_video_mode(dsi, vc);
|
2011-09-05 16:48:27 +05:30
|
|
|
|
2018-02-13 14:00:38 +02:00
|
|
|
dss_mgr_disable(&dsi->output);
|
2013-02-22 12:58:35 +02:00
|
|
|
|
2020-12-15 12:46:51 +02:00
|
|
|
dsi_uninit_dispc(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_update_screen_dispc(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2018-02-11 15:07:34 +02:00
|
|
|
unsigned int bytespp;
|
|
|
|
unsigned int bytespl;
|
|
|
|
unsigned int bytespf;
|
|
|
|
unsigned int total_len;
|
|
|
|
unsigned int packet_payload;
|
|
|
|
unsigned int packet_len;
|
2009-10-28 11:59:56 +02:00
|
|
|
u32 l;
|
OMAP: DSS2: DSI: use a private workqueue
Using the shared workqueue led to to a deadlock in the case where the
display was unblanked via keyboard.
What happens is something like this:
- User presses a key
context 1:
- drivers/char/keyboard.c calls schedule_console_callback()
- fb_unblank takes the console semaphore
- dsi bus lock is taken, and frame transfer is started (dsi bus lock is
left on)
- Unblank code tries to set the panel backlight, which tries to take dsi
bus lock, but is blocked while the frame transfer is going on
context 2, shared workqueue, console_callback in drivers/char/vt.c:
- Tries to take console semaphore
- Blocks, as console semaphore is being held by context 1
- No other shared workqueue work can be run
context 3, HW irq, caused by FRAMEDONE interrupt:
- Interrupt handler schedules framedone-work in shared workqueue
- Framedone-work is never ran, as the shared workqueue is blocked. This
means that the unblank thread stays blocked, which means that context 2
stays blocked.
While I think the real problem is in keyboard/virtual terminal code, using
a private workqueue in the DSI driver is perhaps safer and more robust
than using the shared one. The DSI works should not be delayed more than a
millisecond or so, and even if the private workqueue gives us no hard
promise of doing so, it's still safer bet than the shared workqueue.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
2010-04-12 09:57:19 +03:00
|
|
|
int r;
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
const unsigned vc = dsi->update_vc;
|
2018-02-11 15:07:34 +02:00
|
|
|
const unsigned int line_buf_size = dsi->line_buffer_size;
|
2016-09-22 14:07:04 +03:00
|
|
|
u16 w = dsi->vm.hactive;
|
|
|
|
u16 h = dsi->vm.vactive;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-11-03 16:34:20 +02:00
|
|
|
DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:45:36 +02:00
|
|
|
bytespp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt) / 8;
|
2009-10-28 11:59:56 +02:00
|
|
|
bytespl = w * bytespp;
|
|
|
|
bytespf = bytespl * h;
|
|
|
|
|
|
|
|
/* NOTE: packet_payload has to be equal to N * bytespl, where N is
|
|
|
|
* number of lines in a packet. See errata about VP_CLK_RATIO */
|
|
|
|
|
|
|
|
if (bytespf < line_buf_size)
|
|
|
|
packet_payload = bytespf;
|
|
|
|
else
|
|
|
|
packet_payload = (line_buf_size) / bytespl * bytespl;
|
|
|
|
|
|
|
|
packet_len = packet_payload + 1; /* 1 byte for DCS cmd */
|
|
|
|
total_len = (bytespf / packet_payload) * packet_len;
|
|
|
|
|
|
|
|
if (bytespf % packet_payload)
|
|
|
|
total_len += (bytespf % packet_payload) + 1;
|
|
|
|
|
|
|
|
l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VC_TE(vc), l);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
dsi_vc_write_long_header(dsi, vc, dsi->dsidev->channel, MIPI_DSI_DCS_LONG_WRITE,
|
2011-05-12 17:26:26 +05:30
|
|
|
packet_len, 0);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->te_enabled)
|
2009-10-28 11:59:56 +02:00
|
|
|
l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
|
|
|
|
else
|
|
|
|
l = FLD_MOD(l, 1, 31, 31); /* TE_START */
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_write_reg(dsi, DSI_VC_TE(vc), l);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
/* We put SIDLEMODE to no-idle for the duration of the transfer,
|
|
|
|
* because DSS interrupts are not capable of waking up the CPU and the
|
|
|
|
* framedone interrupt could be delayed for quite a long time. I think
|
|
|
|
* the same goes for any DSS interrupts, but for some reason I have not
|
|
|
|
* seen the problem anywhere else than here.
|
|
|
|
*/
|
2018-02-13 14:00:43 +02:00
|
|
|
dispc_disable_sidle(dsi->dss->dispc);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_perf_mark_start(dsi);
|
2010-01-12 14:16:41 +02:00
|
|
|
|
2011-05-16 15:17:07 +05:30
|
|
|
r = schedule_delayed_work(&dsi->framedone_timeout_work,
|
|
|
|
msecs_to_jiffies(250));
|
OMAP: DSS2: DSI: use a private workqueue
Using the shared workqueue led to to a deadlock in the case where the
display was unblanked via keyboard.
What happens is something like this:
- User presses a key
context 1:
- drivers/char/keyboard.c calls schedule_console_callback()
- fb_unblank takes the console semaphore
- dsi bus lock is taken, and frame transfer is started (dsi bus lock is
left on)
- Unblank code tries to set the panel backlight, which tries to take dsi
bus lock, but is blocked while the frame transfer is going on
context 2, shared workqueue, console_callback in drivers/char/vt.c:
- Tries to take console semaphore
- Blocks, as console semaphore is being held by context 1
- No other shared workqueue work can be run
context 3, HW irq, caused by FRAMEDONE interrupt:
- Interrupt handler schedules framedone-work in shared workqueue
- Framedone-work is never ran, as the shared workqueue is blocked. This
means that the unblank thread stays blocked, which means that context 2
stays blocked.
While I think the real problem is in keyboard/virtual terminal code, using
a private workqueue in the DSI driver is perhaps safer and more robust
than using the shared one. The DSI works should not be delayed more than a
millisecond or so, and even if the private workqueue gives us no hard
promise of doing so, it's still safer bet than the shared workqueue.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
2010-04-12 09:57:19 +03:00
|
|
|
BUG_ON(r == 0);
|
2010-01-12 14:16:41 +02:00
|
|
|
|
2018-02-13 14:00:38 +02:00
|
|
|
dss_mgr_start_update(&dsi->output);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->te_enabled) {
|
2009-10-28 11:59:56 +02:00
|
|
|
/* disable LP_RX_TO, so that we can receive TE. Time to wait
|
|
|
|
* for TE is longer than the timer allows */
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
|
2009-10-28 11:59:56 +02:00
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi_vc_send_bta(dsi, vc);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
2011-05-12 17:26:27 +05:30
|
|
|
mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250));
|
2009-10-28 11:59:56 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-16 14:43:17 -07:00
|
|
|
static void dsi_te_timeout(struct timer_list *unused)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
DSSERR("TE not received for 250ms!\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static void dsi_handle_framedone(struct dsi_data *dsi, int error)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
/* SIDLEMODE back to smart-idle */
|
2018-02-13 14:00:43 +02:00
|
|
|
dispc_enable_sidle(dsi->dss->dispc);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->te_enabled) {
|
2010-01-12 14:16:41 +02:00
|
|
|
/* enable LP_RX_TO again after the TE */
|
2018-02-13 14:00:33 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:59 +02:00
|
|
|
dsi_bus_unlock(dsi);
|
|
|
|
|
2010-06-09 15:31:01 +03:00
|
|
|
if (!error)
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_perf_show(dsi, "DISPC");
|
2010-01-12 14:16:41 +02:00
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2010-06-09 15:31:01 +03:00
|
|
|
static void dsi_framedone_timeout_work_callback(struct work_struct *work)
|
2010-01-12 14:16:41 +02:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
struct dsi_data *dsi = container_of(work, struct dsi_data,
|
|
|
|
framedone_timeout_work.work);
|
2010-06-09 15:31:01 +03:00
|
|
|
/* XXX While extremely unlikely, we could get FRAMEDONE interrupt after
|
|
|
|
* 250ms which would conflict with this timeout work. What should be
|
|
|
|
* done is first cancel the transfer on the HW, and then cancel the
|
|
|
|
* possibly scheduled framedone work. However, cancelling the transfer
|
|
|
|
* on the HW is buggy, and would probably require resetting the whole
|
|
|
|
* DSI */
|
2010-01-12 14:16:41 +02:00
|
|
|
|
2010-06-09 15:31:01 +03:00
|
|
|
DSSERR("Framedone not received for 250ms!\n");
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_handle_framedone(dsi, -ETIMEDOUT);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2012-10-10 13:59:07 +03:00
|
|
|
static void dsi_framedone_irq_callback(void *data)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2018-02-13 14:00:33 +02:00
|
|
|
struct dsi_data *dsi = data;
|
2011-05-12 17:26:27 +05:30
|
|
|
|
2010-06-09 15:31:01 +03:00
|
|
|
/* Note: We get FRAMEDONE when DISPC has finished sending pixels and
|
|
|
|
* turns itself off. However, DSI still has the pixels in its buffers,
|
|
|
|
* and is sending the data.
|
|
|
|
*/
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2012-08-21 13:18:24 -07:00
|
|
|
cancel_delayed_work(&dsi->framedone_timeout_work);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:02 +02:00
|
|
|
DSSDBG("Framedone received!\n");
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_handle_framedone(dsi, 0);
|
2010-01-12 14:16:41 +02:00
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:45:57 +02:00
|
|
|
static int _dsi_update(struct dsi_data *dsi)
|
|
|
|
{
|
|
|
|
dsi_perf_mark_setup(dsi);
|
|
|
|
|
|
|
|
#ifdef DSI_PERF_MEASURE
|
|
|
|
dsi->update_bytes = dsi->vm.hactive * dsi->vm.vactive *
|
|
|
|
mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt) / 8;
|
|
|
|
#endif
|
|
|
|
dsi_update_screen_dispc(dsi);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static int _dsi_send_nop(struct dsi_data *dsi, int vc, int channel)
|
2020-12-15 12:46:34 +02:00
|
|
|
{
|
|
|
|
const u8 payload[] = { MIPI_DCS_NOP };
|
|
|
|
const struct mipi_dsi_msg msg = {
|
|
|
|
.channel = channel,
|
|
|
|
.type = MIPI_DSI_DCS_SHORT_WRITE,
|
|
|
|
.tx_len = 1,
|
|
|
|
.tx_buf = payload,
|
|
|
|
};
|
2020-12-15 12:46:02 +02:00
|
|
|
|
|
|
|
WARN_ON(!dsi_bus_is_locked(dsi));
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
return _omap_dsi_host_transfer(dsi, vc, &msg);
|
2020-12-15 12:46:02 +02:00
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
static int dsi_update_channel(struct omap_dss_device *dssdev, int vc)
|
2010-01-12 14:16:41 +02:00
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
2020-12-15 12:46:02 +02:00
|
|
|
int r;
|
|
|
|
|
2020-12-15 12:45:59 +02:00
|
|
|
dsi_bus_lock(dsi);
|
2020-12-15 12:46:02 +02:00
|
|
|
|
2020-12-15 12:46:05 +02:00
|
|
|
if (!dsi->video_enabled) {
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:02 +02:00
|
|
|
if (dsi->vm.hactive == 0 || dsi->vm.vactive == 0) {
|
|
|
|
r = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
DSSDBG("dsi_update_channel: %d", vc);
|
2020-12-15 12:46:02 +02:00
|
|
|
|
2020-12-15 12:46:34 +02:00
|
|
|
/*
|
|
|
|
* Send NOP between the frames. If we don't send something here, the
|
|
|
|
* updates stop working. This is probably related to DSI spec stating
|
|
|
|
* that the DSI host should transition to LP at least once per frame.
|
|
|
|
*/
|
2020-12-15 12:46:52 +02:00
|
|
|
r = _dsi_send_nop(dsi, VC_CMD, dsi->dsidev->channel);
|
2020-12-15 12:46:02 +02:00
|
|
|
if (r < 0) {
|
2020-12-15 12:46:34 +02:00
|
|
|
DSSWARN("failed to send nop between frames: %d\n", r);
|
2020-12-15 12:46:02 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
drm/omap: dsi: rename 'channel' to 'vc'
The "channel" usage in omap dsi driver is confusing. We have three
different "channels":
1) DSI virtual channel ID. This is a number from 0 to 3, included in the
packet payload.
2) VC. This is a register block in the DSI IP. There are four of those
blocks. A VC is a DSI "pipeline", with defined fifo settings, data
source (cpu or dispc), and some other settings. It has no relation to
the 1).
3) dispc channel. It's the "pipeline" number dispc uses to send pixel
data.
The previous patch handled the third case.
To start fixing 1) and 2), we first rename all uses of 'channel' to
'vc', as in most of the cases that is the correct thing to use.
However, in some places 1) and 2) have gotten mixed up (i.e. the code
uses msg->channel when it should use vc), which will be fixed in the
following patch.
Note that mixing 1) and 2) currently is "fine", as at the moment we only
support DSI peripherals with DSI virtual channel 0, and we always use
VC0 to send data. So both 1) and 2) are always 0.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-66-tomi.valkeinen@ti.com
2020-12-15 12:46:38 +02:00
|
|
|
dsi->update_vc = vc;
|
2010-07-14 14:11:50 +02:00
|
|
|
|
2020-12-15 12:45:57 +02:00
|
|
|
if (dsi->te_enabled && dsi->te_gpio) {
|
|
|
|
schedule_delayed_work(&dsi->te_timeout_work,
|
|
|
|
msecs_to_jiffies(250));
|
|
|
|
atomic_set(&dsi->do_ext_te_update, 1);
|
|
|
|
} else {
|
|
|
|
_dsi_update(dsi);
|
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
return 0;
|
2020-12-15 12:46:02 +02:00
|
|
|
|
|
|
|
err:
|
|
|
|
dsi_bus_unlock(dsi);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsi_update_all(struct omap_dss_device *dssdev)
|
|
|
|
{
|
2020-12-15 12:46:43 +02:00
|
|
|
return dsi_update_channel(dssdev, VC_VIDEO);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Display funcs */
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_configure_dispc_clocks(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2012-06-29 14:31:07 +05:30
|
|
|
struct dispc_clock_info dispc_cinfo;
|
2009-10-28 11:59:56 +02:00
|
|
|
int r;
|
2013-03-07 11:21:45 +02:00
|
|
|
unsigned long fck;
|
2012-06-29 14:31:07 +05:30
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
fck = dsi_get_pll_hsdiv_dispc_rate(dsi);
|
2012-06-29 14:31:07 +05:30
|
|
|
|
2012-11-27 17:05:54 +02:00
|
|
|
dispc_cinfo.lck_div = dsi->user_dispc_cinfo.lck_div;
|
|
|
|
dispc_cinfo.pck_div = dsi->user_dispc_cinfo.pck_div;
|
2012-06-29 14:31:07 +05:30
|
|
|
|
2018-02-13 14:00:43 +02:00
|
|
|
r = dispc_calc_clock_rates(dsi->dss->dispc, fck, &dispc_cinfo);
|
2012-06-29 14:31:07 +05:30
|
|
|
if (r) {
|
|
|
|
DSSERR("Failed to calc dispc clocks\n");
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi->mgr_config.clock_info = dispc_cinfo;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:51 +02:00
|
|
|
static int dsi_init_dispc(struct dsi_data *dsi)
|
2012-06-29 14:31:07 +05:30
|
|
|
{
|
2020-12-15 12:46:37 +02:00
|
|
|
enum omap_channel dispc_channel = dsi->output.dispc_channel;
|
2012-06-29 14:31:07 +05:30
|
|
|
int r;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:37 +02:00
|
|
|
dss_select_lcd_clk_source(dsi->dss, dispc_channel, dsi->module_id == 0 ?
|
2016-05-17 14:01:10 +03:00
|
|
|
DSS_CLK_SRC_PLL1_1 :
|
|
|
|
DSS_CLK_SRC_PLL2_1);
|
2011-11-03 16:34:20 +02:00
|
|
|
|
2012-08-16 18:02:00 +05:30
|
|
|
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) {
|
2018-02-13 14:00:38 +02:00
|
|
|
r = dss_mgr_register_framedone_handler(&dsi->output,
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_framedone_irq_callback, dsi);
|
2011-09-05 16:48:27 +05:30
|
|
|
if (r) {
|
2012-10-10 13:59:07 +03:00
|
|
|
DSSERR("can't register FRAMEDONE handler\n");
|
2012-06-29 14:31:07 +05:30
|
|
|
goto err;
|
2011-09-05 16:48:27 +05:30
|
|
|
}
|
|
|
|
|
2012-06-29 14:31:07 +05:30
|
|
|
dsi->mgr_config.stallmode = true;
|
|
|
|
dsi->mgr_config.fifohandcheck = true;
|
2011-09-05 16:48:27 +05:30
|
|
|
} else {
|
2012-06-29 14:31:07 +05:30
|
|
|
dsi->mgr_config.stallmode = false;
|
|
|
|
dsi->mgr_config.fifohandcheck = false;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_configure_dispc_clocks(dsi);
|
2012-06-29 14:31:07 +05:30
|
|
|
if (r)
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
|
|
|
|
dsi->mgr_config.video_port_width =
|
2020-12-15 12:45:36 +02:00
|
|
|
mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
|
2012-06-29 14:31:07 +05:30
|
|
|
dsi->mgr_config.lcden_sig_polarity = 0;
|
|
|
|
|
2018-02-13 14:00:38 +02:00
|
|
|
dss_mgr_set_lcd_config(&dsi->output, &dsi->mgr_config);
|
2012-06-21 09:45:11 +05:30
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
return 0;
|
2012-06-29 14:31:07 +05:30
|
|
|
err1:
|
2012-08-16 18:02:00 +05:30
|
|
|
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
|
2018-02-13 14:00:38 +02:00
|
|
|
dss_mgr_unregister_framedone_handler(&dsi->output,
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_framedone_irq_callback, dsi);
|
2012-06-29 14:31:07 +05:30
|
|
|
err:
|
2020-12-15 12:46:37 +02:00
|
|
|
dss_select_lcd_clk_source(dsi->dss, dispc_channel, DSS_CLK_SRC_FCK);
|
2012-06-29 14:31:07 +05:30
|
|
|
return r;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:51 +02:00
|
|
|
static void dsi_uninit_dispc(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2020-12-15 12:46:37 +02:00
|
|
|
enum omap_channel dispc_channel = dsi->output.dispc_channel;
|
2018-02-13 14:00:37 +02:00
|
|
|
|
2012-10-10 13:59:07 +03:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
|
2018-02-13 14:00:38 +02:00
|
|
|
dss_mgr_unregister_framedone_handler(&dsi->output,
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_framedone_irq_callback, dsi);
|
2013-02-22 12:58:35 +02:00
|
|
|
|
2020-12-15 12:46:37 +02:00
|
|
|
dss_select_lcd_clk_source(dsi->dss, dispc_channel, DSS_CLK_SRC_FCK);
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_configure_dsi_clocks(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2014-10-22 14:49:14 +03:00
|
|
|
struct dss_pll_clock_info cinfo;
|
2009-10-28 11:59:56 +02:00
|
|
|
int r;
|
|
|
|
|
2012-11-27 17:05:54 +02:00
|
|
|
cinfo = dsi->user_dsi_cinfo;
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
r = dss_pll_set_config(&dsi->pll, &cinfo);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (r) {
|
|
|
|
DSSERR("Failed to set dsi clocks\n");
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:52 +02:00
|
|
|
static void dsi_setup_dsi_vcs(struct dsi_data *dsi)
|
|
|
|
{
|
|
|
|
/* Setup VC_CMD for LP and cpu transfers */
|
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_CMD), 0, 9, 9); /* LP */
|
|
|
|
|
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_CMD), 0, 1, 1); /* SOURCE_L4 */
|
|
|
|
dsi->vc[VC_CMD].source = DSI_VC_SOURCE_L4;
|
|
|
|
|
|
|
|
/* Setup VC_VIDEO for HS and dispc transfers */
|
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_VIDEO), 1, 9, 9); /* HS */
|
|
|
|
|
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_VIDEO), 1, 1, 1); /* SOURCE_VP */
|
|
|
|
dsi->vc[VC_VIDEO].source = DSI_VC_SOURCE_VP;
|
|
|
|
|
2020-12-15 12:46:56 +02:00
|
|
|
if ((dsi->data->quirks & DSI_QUIRK_DCS_CMD_CONFIG_VC) &&
|
|
|
|
!(dsi->dsidev->mode_flags & MIPI_DSI_MODE_VIDEO))
|
2020-12-15 12:46:52 +02:00
|
|
|
REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_VIDEO), 1, 30, 30); /* DCS_CMD_ENABLE */
|
|
|
|
|
|
|
|
dsi_vc_enable(dsi, VC_CMD, 1);
|
|
|
|
dsi_vc_enable(dsi, VC_VIDEO, 1);
|
|
|
|
|
|
|
|
dsi_if_enable(dsi, 1);
|
|
|
|
|
|
|
|
dsi_force_tx_stop_mode_io(dsi);
|
|
|
|
|
|
|
|
/* start the DDR clock by sending a NULL packet */
|
2020-12-15 12:46:54 +02:00
|
|
|
if (!(dsi->dsidev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
|
2020-12-15 12:46:52 +02:00
|
|
|
dsi_vc_send_null(dsi, VC_CMD, dsi->dsidev->channel);
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:51 +02:00
|
|
|
static int dsi_init_dsi(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
r = dss_pll_enable(&dsi->pll);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (r)
|
2019-02-07 07:45:16 -08:00
|
|
|
return r;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_configure_dsi_clocks(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (r)
|
2019-02-07 07:45:16 -08:00
|
|
|
goto err0;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:24 +02:00
|
|
|
dss_select_dsi_clk_source(dsi->dss, dsi->module_id,
|
|
|
|
dsi->module_id == 0 ?
|
|
|
|
DSS_CLK_SRC_PLL1_2 : DSS_CLK_SRC_PLL2_2);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
DSSDBG("PLL OK\n");
|
|
|
|
|
2019-02-07 07:45:16 -08:00
|
|
|
if (!dsi->vdds_dsi_enabled) {
|
|
|
|
r = regulator_enable(dsi->vdds_dsi_reg);
|
|
|
|
if (r)
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
dsi->vdds_dsi_enabled = true;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_cio_init(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (r)
|
|
|
|
goto err2;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
_dsi_print_reset_status(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_proto_timings(dsi);
|
|
|
|
dsi_set_lp_clk_divisor(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
if (1)
|
2018-02-13 14:00:33 +02:00
|
|
|
_dsi_print_reset_status(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_proto_config(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (r)
|
|
|
|
goto err3;
|
|
|
|
|
2020-12-15 12:46:52 +02:00
|
|
|
dsi_setup_dsi_vcs(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
err3:
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_cio_uninit(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
err2:
|
2019-02-07 07:45:16 -08:00
|
|
|
regulator_disable(dsi->vdds_dsi_reg);
|
|
|
|
dsi->vdds_dsi_enabled = false;
|
2009-10-28 11:59:56 +02:00
|
|
|
err1:
|
2019-02-07 07:45:16 -08:00
|
|
|
dss_select_dsi_clk_source(dsi->dss, dsi->module_id, DSS_CLK_SRC_FCK);
|
2009-10-28 11:59:56 +02:00
|
|
|
err0:
|
2019-02-07 07:45:16 -08:00
|
|
|
dss_pll_disable(&dsi->pll);
|
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:55 +02:00
|
|
|
static void dsi_uninit_dsi(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2010-04-22 22:50:09 +02:00
|
|
|
/* disable interface */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_if_enable(dsi, 0);
|
|
|
|
dsi_vc_enable(dsi, 0, 0);
|
|
|
|
dsi_vc_enable(dsi, 1, 0);
|
|
|
|
dsi_vc_enable(dsi, 2, 0);
|
|
|
|
dsi_vc_enable(dsi, 3, 0);
|
2010-04-22 22:50:09 +02:00
|
|
|
|
2018-02-13 14:00:24 +02:00
|
|
|
dss_select_dsi_clk_source(dsi->dss, dsi->module_id, DSS_CLK_SRC_FCK);
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_cio_uninit(dsi);
|
2019-02-07 07:45:16 -08:00
|
|
|
dss_pll_disable(&dsi->pll);
|
|
|
|
|
2020-12-15 12:46:55 +02:00
|
|
|
regulator_disable(dsi->vdds_dsi_reg);
|
|
|
|
dsi->vdds_dsi_enabled = false;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:51 +02:00
|
|
|
static void dsi_enable(struct dsi_data *dsi)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2018-08-24 19:38:07 +03:00
|
|
|
int r;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:00 +02:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsi));
|
2010-01-12 15:12:07 +02:00
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
if (WARN_ON(dsi->iface_enabled))
|
|
|
|
return;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
mutex_lock(&dsi->lock);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_runtime_get(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (r)
|
2011-05-27 10:52:19 +03:00
|
|
|
goto err_get_dsi;
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
_dsi_initialize_irq(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2020-12-15 12:46:51 +02:00
|
|
|
r = dsi_init_dsi(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
if (r)
|
2011-05-27 10:52:19 +03:00
|
|
|
goto err_init_dsi;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
dsi->iface_enabled = true;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
mutex_unlock(&dsi->lock);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-08-24 19:38:07 +03:00
|
|
|
return;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2011-05-27 10:52:19 +03:00
|
|
|
err_init_dsi:
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_runtime_put(dsi);
|
2011-05-27 10:52:19 +03:00
|
|
|
err_get_dsi:
|
2011-05-12 17:26:27 +05:30
|
|
|
mutex_unlock(&dsi->lock);
|
2020-12-15 12:46:51 +02:00
|
|
|
DSSDBG("dsi_enable FAILED\n");
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:55 +02:00
|
|
|
static void dsi_disable(struct dsi_data *dsi)
|
2020-12-15 12:46:00 +02:00
|
|
|
{
|
|
|
|
WARN_ON(!dsi_bus_is_locked(dsi));
|
2009-10-28 11:59:56 +02:00
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
if (WARN_ON(!dsi->iface_enabled))
|
|
|
|
return;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
mutex_lock(&dsi->lock);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_sync_vc(dsi, 0);
|
|
|
|
dsi_sync_vc(dsi, 1);
|
|
|
|
dsi_sync_vc(dsi, 2);
|
|
|
|
dsi_sync_vc(dsi, 3);
|
2011-06-16 14:34:06 +03:00
|
|
|
|
2020-12-15 12:46:55 +02:00
|
|
|
dsi_uninit_dsi(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_runtime_put(dsi);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
dsi->iface_enabled = false;
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
mutex_unlock(&dsi->lock);
|
2020-12-15 12:46:00 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:58 +02:00
|
|
|
static int dsi_enable_te(struct dsi_data *dsi, bool enable)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2011-05-12 17:26:27 +05:30
|
|
|
dsi->te_enabled = enable;
|
2020-12-15 12:45:57 +02:00
|
|
|
|
|
|
|
if (dsi->te_gpio) {
|
|
|
|
if (enable)
|
|
|
|
enable_irq(dsi->te_irq);
|
|
|
|
else
|
|
|
|
disable_irq(dsi->te_irq);
|
|
|
|
}
|
|
|
|
|
2010-01-11 15:11:01 +02:00
|
|
|
return 0;
|
2009-10-28 11:59:56 +02:00
|
|
|
}
|
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
#ifdef PRINT_VERBOSE_VM_TIMINGS
|
|
|
|
static void print_dsi_vm(const char *str,
|
|
|
|
const struct omap_dss_dsi_videomode_timings *t)
|
|
|
|
{
|
|
|
|
unsigned long byteclk = t->hsclk / 4;
|
|
|
|
int bl, wc, pps, tot;
|
|
|
|
|
|
|
|
wc = DIV_ROUND_UP(t->hact * t->bitspp, 8);
|
|
|
|
pps = DIV_ROUND_UP(wc + 6, t->ndl); /* pixel packet size */
|
2016-12-26 20:23:19 +01:00
|
|
|
bl = t->hss + t->hsa + t->hse + t->hbp + t->hfp;
|
2013-03-05 17:21:35 +02:00
|
|
|
tot = bl + pps;
|
|
|
|
|
|
|
|
#define TO_DSI_T(x) ((u32)div64_u64((u64)x * 1000000000llu, byteclk))
|
|
|
|
|
|
|
|
pr_debug("%s bck %lu, %u/%u/%u/%u/%u/%u = %u+%u = %u, "
|
|
|
|
"%u/%u/%u/%u/%u/%u = %u + %u = %u\n",
|
|
|
|
str,
|
|
|
|
byteclk,
|
2016-12-26 20:23:19 +01:00
|
|
|
t->hss, t->hsa, t->hse, t->hbp, pps, t->hfp,
|
2013-03-05 17:21:35 +02:00
|
|
|
bl, pps, tot,
|
|
|
|
TO_DSI_T(t->hss),
|
|
|
|
TO_DSI_T(t->hsa),
|
|
|
|
TO_DSI_T(t->hse),
|
|
|
|
TO_DSI_T(t->hbp),
|
|
|
|
TO_DSI_T(pps),
|
2016-12-26 20:23:19 +01:00
|
|
|
TO_DSI_T(t->hfp),
|
2013-03-05 17:21:35 +02:00
|
|
|
|
|
|
|
TO_DSI_T(bl),
|
|
|
|
TO_DSI_T(pps),
|
|
|
|
|
|
|
|
TO_DSI_T(tot));
|
|
|
|
#undef TO_DSI_T
|
|
|
|
}
|
|
|
|
|
2016-09-22 14:07:04 +03:00
|
|
|
static void print_dispc_vm(const char *str, const struct videomode *vm)
|
2013-03-05 17:21:35 +02:00
|
|
|
{
|
2016-09-22 14:07:04 +03:00
|
|
|
unsigned long pck = vm->pixelclock;
|
2013-03-05 17:21:35 +02:00
|
|
|
int hact, bl, tot;
|
|
|
|
|
2016-09-22 14:07:04 +03:00
|
|
|
hact = vm->hactive;
|
2016-12-26 20:23:19 +01:00
|
|
|
bl = vm->hsync_len + vm->hback_porch + vm->hfront_porch;
|
2013-03-05 17:21:35 +02:00
|
|
|
tot = hact + bl;
|
|
|
|
|
|
|
|
#define TO_DISPC_T(x) ((u32)div64_u64((u64)x * 1000000000llu, pck))
|
|
|
|
|
|
|
|
pr_debug("%s pck %lu, %u/%u/%u/%u = %u+%u = %u, "
|
|
|
|
"%u/%u/%u/%u = %u + %u = %u\n",
|
|
|
|
str,
|
|
|
|
pck,
|
2016-12-26 20:23:19 +01:00
|
|
|
vm->hsync_len, vm->hback_porch, hact, vm->hfront_porch,
|
2013-03-05 17:21:35 +02:00
|
|
|
bl, hact, tot,
|
2016-09-22 14:07:04 +03:00
|
|
|
TO_DISPC_T(vm->hsync_len),
|
2016-12-26 20:23:19 +01:00
|
|
|
TO_DISPC_T(vm->hback_porch),
|
2013-03-05 17:21:35 +02:00
|
|
|
TO_DISPC_T(hact),
|
2016-09-22 14:07:04 +03:00
|
|
|
TO_DISPC_T(vm->hfront_porch),
|
2013-03-05 17:21:35 +02:00
|
|
|
TO_DISPC_T(bl),
|
|
|
|
TO_DISPC_T(hact),
|
|
|
|
TO_DISPC_T(tot));
|
|
|
|
#undef TO_DISPC_T
|
|
|
|
}
|
|
|
|
|
|
|
|
/* note: this is not quite accurate */
|
|
|
|
static void print_dsi_dispc_vm(const char *str,
|
|
|
|
const struct omap_dss_dsi_videomode_timings *t)
|
|
|
|
{
|
2016-09-22 14:07:03 +03:00
|
|
|
struct videomode vm = { 0 };
|
2013-03-05 17:21:35 +02:00
|
|
|
unsigned long byteclk = t->hsclk / 4;
|
|
|
|
unsigned long pck;
|
|
|
|
u64 dsi_tput;
|
|
|
|
int dsi_hact, dsi_htot;
|
|
|
|
|
|
|
|
dsi_tput = (u64)byteclk * t->ndl * 8;
|
|
|
|
pck = (u32)div64_u64(dsi_tput, t->bitspp);
|
|
|
|
dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl);
|
2016-12-26 20:23:19 +01:00
|
|
|
dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
2013-04-10 14:12:14 +03:00
|
|
|
vm.pixelclock = pck;
|
2016-09-22 14:06:48 +03:00
|
|
|
vm.hsync_len = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk);
|
2016-12-26 20:23:19 +01:00
|
|
|
vm.hback_porch = div64_u64((u64)t->hbp * pck, byteclk);
|
|
|
|
vm.hfront_porch = div64_u64((u64)t->hfp * pck, byteclk);
|
2016-09-22 14:06:46 +03:00
|
|
|
vm.hactive = t->hact;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
|
|
|
print_dispc_vm(str, &vm);
|
|
|
|
}
|
|
|
|
#endif /* PRINT_VERBOSE_VM_TIMINGS */
|
|
|
|
|
|
|
|
static bool dsi_cm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
|
|
|
|
unsigned long pck, void *data)
|
2012-08-13 14:17:30 +05:30
|
|
|
{
|
2013-03-05 17:21:35 +02:00
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
2016-09-22 14:07:04 +03:00
|
|
|
struct videomode *vm = &ctx->vm;
|
2012-08-13 14:17:30 +05:30
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
ctx->dispc_cinfo.lck_div = lckd;
|
|
|
|
ctx->dispc_cinfo.pck_div = pckd;
|
|
|
|
ctx->dispc_cinfo.lck = lck;
|
|
|
|
ctx->dispc_cinfo.pck = pck;
|
2012-08-13 14:17:30 +05:30
|
|
|
|
2016-09-22 14:07:04 +03:00
|
|
|
*vm = *ctx->config->vm;
|
|
|
|
vm->pixelclock = pck;
|
|
|
|
vm->hactive = ctx->config->vm->hactive;
|
|
|
|
vm->vactive = ctx->config->vm->vactive;
|
|
|
|
vm->hsync_len = vm->hfront_porch = vm->hback_porch = vm->vsync_len = 1;
|
|
|
|
vm->vfront_porch = vm->vback_porch = 0;
|
2012-08-13 14:17:30 +05:30
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
return true;
|
2012-08-13 14:17:30 +05:30
|
|
|
}
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
static bool dsi_cm_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
|
2013-03-05 17:21:35 +02:00
|
|
|
void *data)
|
2012-08-09 15:23:43 +05:30
|
|
|
{
|
2013-03-05 17:21:35 +02:00
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
2012-08-09 15:23:43 +05:30
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
|
2014-11-07 13:13:24 +02:00
|
|
|
ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
|
2012-08-09 15:23:43 +05:30
|
|
|
|
2018-02-13 14:00:43 +02:00
|
|
|
return dispc_div_calc(ctx->dsi->dss->dispc, dispc,
|
|
|
|
ctx->req_pck_min, ctx->req_pck_max,
|
|
|
|
dsi_cm_calc_dispc_cb, ctx);
|
2013-03-05 17:21:35 +02:00
|
|
|
}
|
2012-08-09 15:23:43 +05:30
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
static bool dsi_cm_calc_pll_cb(int n, int m, unsigned long fint,
|
|
|
|
unsigned long clkdco, void *data)
|
2013-03-05 17:21:35 +02:00
|
|
|
{
|
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
2018-02-13 14:00:33 +02:00
|
|
|
struct dsi_data *dsi = ctx->dsi;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
ctx->dsi_cinfo.n = n;
|
|
|
|
ctx->dsi_cinfo.m = m;
|
2013-03-05 17:21:35 +02:00
|
|
|
ctx->dsi_cinfo.fint = fint;
|
2014-10-22 14:49:14 +03:00
|
|
|
ctx->dsi_cinfo.clkdco = clkdco;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
2016-05-17 21:23:37 +03:00
|
|
|
return dss_pll_hsdiv_calc_a(ctx->pll, clkdco, ctx->req_pck_min,
|
2017-08-05 01:44:15 +03:00
|
|
|
dsi->data->max_fck_freq,
|
2013-03-05 17:21:35 +02:00
|
|
|
dsi_cm_calc_hsdiv_cb, ctx);
|
2012-08-09 15:23:43 +05:30
|
|
|
}
|
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
static bool dsi_cm_calc(struct dsi_data *dsi,
|
|
|
|
const struct omap_dss_dsi_config *cfg,
|
|
|
|
struct dsi_clk_calc_ctx *ctx)
|
2012-08-10 15:01:33 +05:30
|
|
|
{
|
2013-03-05 17:21:35 +02:00
|
|
|
unsigned long clkin;
|
|
|
|
int bitspp, ndl;
|
|
|
|
unsigned long pll_min, pll_max;
|
|
|
|
unsigned long pck, txbyteclk;
|
2012-08-10 15:01:33 +05:30
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
clkin = clk_get_rate(dsi->pll.clkin);
|
2020-12-15 12:45:36 +02:00
|
|
|
bitspp = mipi_dsi_pixel_format_to_bpp(cfg->pixel_format);
|
2013-03-05 17:21:35 +02:00
|
|
|
ndl = dsi->num_lanes_used - 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here we should calculate minimum txbyteclk to be able to send the
|
|
|
|
* frame in time, and also to handle TE. That's not very simple, though,
|
|
|
|
* especially as we go to LP between each pixel packet due to HW
|
|
|
|
* "feature". So let's just estimate very roughly and multiply by 1.5.
|
|
|
|
*/
|
2016-09-22 14:07:04 +03:00
|
|
|
pck = cfg->vm->pixelclock;
|
2013-03-05 17:21:35 +02:00
|
|
|
pck = pck * 3 / 2;
|
|
|
|
txbyteclk = pck * bitspp / 8 / ndl;
|
2012-08-10 15:01:33 +05:30
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
memset(ctx, 0, sizeof(*ctx));
|
2018-02-13 14:00:33 +02:00
|
|
|
ctx->dsi = dsi;
|
2014-10-22 14:49:14 +03:00
|
|
|
ctx->pll = &dsi->pll;
|
2013-03-05 17:21:35 +02:00
|
|
|
ctx->config = cfg;
|
|
|
|
ctx->req_pck_min = pck;
|
|
|
|
ctx->req_pck_nom = pck;
|
|
|
|
ctx->req_pck_max = pck * 3 / 2;
|
2012-08-10 15:01:33 +05:30
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
pll_min = max(cfg->hs_clk_min * 4, txbyteclk * 4 * 4);
|
|
|
|
pll_max = cfg->hs_clk_max * 4;
|
|
|
|
|
2016-05-17 21:23:37 +03:00
|
|
|
return dss_pll_calc_a(ctx->pll, clkin,
|
2013-03-05 17:21:35 +02:00
|
|
|
pll_min, pll_max,
|
|
|
|
dsi_cm_calc_pll_cb, ctx);
|
2012-08-10 15:01:33 +05:30
|
|
|
}
|
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx)
|
2012-08-16 18:02:00 +05:30
|
|
|
{
|
2018-02-13 14:00:33 +02:00
|
|
|
struct dsi_data *dsi = ctx->dsi;
|
2013-03-05 17:21:35 +02:00
|
|
|
const struct omap_dss_dsi_config *cfg = ctx->config;
|
2020-12-15 12:45:36 +02:00
|
|
|
int bitspp = mipi_dsi_pixel_format_to_bpp(cfg->pixel_format);
|
2013-03-05 17:21:35 +02:00
|
|
|
int ndl = dsi->num_lanes_used - 1;
|
2014-11-07 13:08:16 +02:00
|
|
|
unsigned long hsclk = ctx->dsi_cinfo.clkdco / 4;
|
2013-03-05 17:21:35 +02:00
|
|
|
unsigned long byteclk = hsclk / 4;
|
2012-08-16 18:02:00 +05:30
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
unsigned long dispc_pck, req_pck_min, req_pck_nom, req_pck_max;
|
|
|
|
int xres;
|
|
|
|
int panel_htot, panel_hbl; /* pixels */
|
|
|
|
int dispc_htot, dispc_hbl; /* pixels */
|
|
|
|
int dsi_htot, dsi_hact, dsi_hbl, hss, hse; /* byteclks */
|
|
|
|
int hfp, hsa, hbp;
|
2016-09-22 14:07:03 +03:00
|
|
|
const struct videomode *req_vm;
|
|
|
|
struct videomode *dispc_vm;
|
2013-03-05 17:21:35 +02:00
|
|
|
struct omap_dss_dsi_videomode_timings *dsi_vm;
|
|
|
|
u64 dsi_tput, dispc_tput;
|
2012-08-16 18:02:00 +05:30
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
dsi_tput = (u64)byteclk * ndl * 8;
|
2012-08-16 18:02:00 +05:30
|
|
|
|
2016-09-22 14:07:04 +03:00
|
|
|
req_vm = cfg->vm;
|
2013-03-05 17:21:35 +02:00
|
|
|
req_pck_min = ctx->req_pck_min;
|
|
|
|
req_pck_max = ctx->req_pck_max;
|
|
|
|
req_pck_nom = ctx->req_pck_nom;
|
|
|
|
|
|
|
|
dispc_pck = ctx->dispc_cinfo.pck;
|
|
|
|
dispc_tput = (u64)dispc_pck * bitspp;
|
|
|
|
|
2016-09-22 14:06:46 +03:00
|
|
|
xres = req_vm->hactive;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
2016-09-22 14:06:50 +03:00
|
|
|
panel_hbl = req_vm->hfront_porch + req_vm->hback_porch +
|
|
|
|
req_vm->hsync_len;
|
2013-03-05 17:21:35 +02:00
|
|
|
panel_htot = xres + panel_hbl;
|
|
|
|
|
|
|
|
dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(xres * bitspp, 8) + 6, ndl);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When there are no line buffers, DISPC and DSI must have the
|
|
|
|
* same tput. Otherwise DISPC tput needs to be higher than DSI's.
|
|
|
|
*/
|
|
|
|
if (dsi->line_buffer_size < xres * bitspp / 8) {
|
|
|
|
if (dispc_tput != dsi_tput)
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if (dispc_tput < dsi_tput)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DSI tput must be over the min requirement */
|
|
|
|
if (dsi_tput < (u64)bitspp * req_pck_min)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* When non-burst mode, DSI tput must be below max requirement. */
|
|
|
|
if (cfg->trans_mode != OMAP_DSS_DSI_BURST_MODE) {
|
|
|
|
if (dsi_tput > (u64)bitspp * req_pck_max)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hss = DIV_ROUND_UP(4, ndl);
|
|
|
|
|
|
|
|
if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
|
2016-09-22 14:06:48 +03:00
|
|
|
if (ndl == 3 && req_vm->hsync_len == 0)
|
2013-03-05 17:21:35 +02:00
|
|
|
hse = 1;
|
|
|
|
else
|
|
|
|
hse = DIV_ROUND_UP(4, ndl);
|
|
|
|
} else {
|
|
|
|
hse = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DSI htot to match the panel's nominal pck */
|
|
|
|
dsi_htot = div64_u64((u64)panel_htot * byteclk, req_pck_nom);
|
|
|
|
|
|
|
|
/* fail if there would be no time for blanking */
|
|
|
|
if (dsi_htot < hss + hse + dsi_hact)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* total DSI blanking needed to achieve panel's TL */
|
|
|
|
dsi_hbl = dsi_htot - dsi_hact;
|
|
|
|
|
|
|
|
/* DISPC htot to match the DSI TL */
|
|
|
|
dispc_htot = div64_u64((u64)dsi_htot * dispc_pck, byteclk);
|
|
|
|
|
|
|
|
/* verify that the DSI and DISPC TLs are the same */
|
|
|
|
if ((u64)dsi_htot * dispc_pck != (u64)dispc_htot * byteclk)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dispc_hbl = dispc_htot - xres;
|
|
|
|
|
|
|
|
/* setup DSI videomode */
|
|
|
|
|
|
|
|
dsi_vm = &ctx->dsi_vm;
|
|
|
|
memset(dsi_vm, 0, sizeof(*dsi_vm));
|
|
|
|
|
|
|
|
dsi_vm->hsclk = hsclk;
|
|
|
|
|
|
|
|
dsi_vm->ndl = ndl;
|
|
|
|
dsi_vm->bitspp = bitspp;
|
|
|
|
|
|
|
|
if (cfg->trans_mode != OMAP_DSS_DSI_PULSE_MODE) {
|
|
|
|
hsa = 0;
|
2016-09-22 14:06:48 +03:00
|
|
|
} else if (ndl == 3 && req_vm->hsync_len == 0) {
|
2013-03-05 17:21:35 +02:00
|
|
|
hsa = 0;
|
|
|
|
} else {
|
2016-09-22 14:06:48 +03:00
|
|
|
hsa = div64_u64((u64)req_vm->hsync_len * byteclk, req_pck_nom);
|
2013-03-05 17:21:35 +02:00
|
|
|
hsa = max(hsa - hse, 1);
|
|
|
|
}
|
|
|
|
|
2016-09-22 14:06:50 +03:00
|
|
|
hbp = div64_u64((u64)req_vm->hback_porch * byteclk, req_pck_nom);
|
2013-03-05 17:21:35 +02:00
|
|
|
hbp = max(hbp, 1);
|
|
|
|
|
|
|
|
hfp = dsi_hbl - (hss + hsa + hse + hbp);
|
|
|
|
if (hfp < 1) {
|
|
|
|
int t;
|
|
|
|
/* we need to take cycles from hbp */
|
|
|
|
|
|
|
|
t = 1 - hfp;
|
|
|
|
hbp = max(hbp - t, 1);
|
|
|
|
hfp = dsi_hbl - (hss + hsa + hse + hbp);
|
|
|
|
|
|
|
|
if (hfp < 1 && hsa > 0) {
|
|
|
|
/* we need to take cycles from hsa */
|
|
|
|
t = 1 - hfp;
|
|
|
|
hsa = max(hsa - t, 1);
|
|
|
|
hfp = dsi_hbl - (hss + hsa + hse + hbp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hfp < 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dsi_vm->hss = hss;
|
|
|
|
dsi_vm->hsa = hsa;
|
|
|
|
dsi_vm->hse = hse;
|
|
|
|
dsi_vm->hbp = hbp;
|
|
|
|
dsi_vm->hact = xres;
|
|
|
|
dsi_vm->hfp = hfp;
|
|
|
|
|
2016-09-22 14:06:51 +03:00
|
|
|
dsi_vm->vsa = req_vm->vsync_len;
|
2016-09-22 14:06:53 +03:00
|
|
|
dsi_vm->vbp = req_vm->vback_porch;
|
2016-09-22 14:06:47 +03:00
|
|
|
dsi_vm->vact = req_vm->vactive;
|
2016-09-22 14:06:52 +03:00
|
|
|
dsi_vm->vfp = req_vm->vfront_porch;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
|
|
|
dsi_vm->trans_mode = cfg->trans_mode;
|
|
|
|
|
|
|
|
dsi_vm->blanking_mode = 0;
|
|
|
|
dsi_vm->hsa_blanking_mode = 1;
|
|
|
|
dsi_vm->hfp_blanking_mode = 1;
|
|
|
|
dsi_vm->hbp_blanking_mode = 1;
|
|
|
|
|
|
|
|
dsi_vm->window_sync = 4;
|
|
|
|
|
|
|
|
/* setup DISPC videomode */
|
|
|
|
|
2016-09-22 14:07:04 +03:00
|
|
|
dispc_vm = &ctx->vm;
|
2013-03-05 17:21:35 +02:00
|
|
|
*dispc_vm = *req_vm;
|
2013-04-10 14:12:14 +03:00
|
|
|
dispc_vm->pixelclock = dispc_pck;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
|
|
|
if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
|
2016-09-22 14:06:48 +03:00
|
|
|
hsa = div64_u64((u64)req_vm->hsync_len * dispc_pck,
|
2013-03-05 17:21:35 +02:00
|
|
|
req_pck_nom);
|
|
|
|
hsa = max(hsa, 1);
|
|
|
|
} else {
|
|
|
|
hsa = 1;
|
|
|
|
}
|
|
|
|
|
2016-09-22 14:06:50 +03:00
|
|
|
hbp = div64_u64((u64)req_vm->hback_porch * dispc_pck, req_pck_nom);
|
2013-03-05 17:21:35 +02:00
|
|
|
hbp = max(hbp, 1);
|
|
|
|
|
|
|
|
hfp = dispc_hbl - hsa - hbp;
|
|
|
|
if (hfp < 1) {
|
|
|
|
int t;
|
|
|
|
/* we need to take cycles from hbp */
|
|
|
|
|
|
|
|
t = 1 - hfp;
|
|
|
|
hbp = max(hbp - t, 1);
|
|
|
|
hfp = dispc_hbl - hsa - hbp;
|
|
|
|
|
|
|
|
if (hfp < 1) {
|
|
|
|
/* we need to take cycles from hsa */
|
|
|
|
t = 1 - hfp;
|
|
|
|
hsa = max(hsa - t, 1);
|
|
|
|
hfp = dispc_hbl - hsa - hbp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hfp < 1)
|
|
|
|
return false;
|
|
|
|
|
2016-09-22 14:06:49 +03:00
|
|
|
dispc_vm->hfront_porch = hfp;
|
2016-09-22 14:06:48 +03:00
|
|
|
dispc_vm->hsync_len = hsa;
|
2016-09-22 14:06:50 +03:00
|
|
|
dispc_vm->hback_porch = hbp;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool dsi_vm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
|
|
|
|
unsigned long pck, void *data)
|
|
|
|
{
|
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
|
|
|
|
|
|
|
ctx->dispc_cinfo.lck_div = lckd;
|
|
|
|
ctx->dispc_cinfo.pck_div = pckd;
|
|
|
|
ctx->dispc_cinfo.lck = lck;
|
|
|
|
ctx->dispc_cinfo.pck = pck;
|
|
|
|
|
|
|
|
if (dsi_vm_calc_blanking(ctx) == false)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
#ifdef PRINT_VERBOSE_VM_TIMINGS
|
2016-09-22 14:07:04 +03:00
|
|
|
print_dispc_vm("dispc", &ctx->vm);
|
2013-03-05 17:21:35 +02:00
|
|
|
print_dsi_vm("dsi ", &ctx->dsi_vm);
|
2016-09-22 14:07:04 +03:00
|
|
|
print_dispc_vm("req ", ctx->config->vm);
|
2013-03-05 17:21:35 +02:00
|
|
|
print_dsi_dispc_vm("act ", &ctx->dsi_vm);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
static bool dsi_vm_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
|
2013-03-05 17:21:35 +02:00
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
|
|
|
unsigned long pck_max;
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
|
2014-11-07 13:13:24 +02:00
|
|
|
ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In burst mode we can let the dispc pck be arbitrarily high, but it
|
|
|
|
* limits our scaling abilities. So for now, don't aim too high.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ctx->config->trans_mode == OMAP_DSS_DSI_BURST_MODE)
|
|
|
|
pck_max = ctx->req_pck_max + 10000000;
|
|
|
|
else
|
|
|
|
pck_max = ctx->req_pck_max;
|
|
|
|
|
2018-02-13 14:00:43 +02:00
|
|
|
return dispc_div_calc(ctx->dsi->dss->dispc, dispc,
|
|
|
|
ctx->req_pck_min, pck_max,
|
|
|
|
dsi_vm_calc_dispc_cb, ctx);
|
2013-03-05 17:21:35 +02:00
|
|
|
}
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
static bool dsi_vm_calc_pll_cb(int n, int m, unsigned long fint,
|
|
|
|
unsigned long clkdco, void *data)
|
2013-03-05 17:21:35 +02:00
|
|
|
{
|
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
2018-02-13 14:00:33 +02:00
|
|
|
struct dsi_data *dsi = ctx->dsi;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
ctx->dsi_cinfo.n = n;
|
|
|
|
ctx->dsi_cinfo.m = m;
|
2013-03-05 17:21:35 +02:00
|
|
|
ctx->dsi_cinfo.fint = fint;
|
2014-10-22 14:49:14 +03:00
|
|
|
ctx->dsi_cinfo.clkdco = clkdco;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
2016-05-17 21:23:37 +03:00
|
|
|
return dss_pll_hsdiv_calc_a(ctx->pll, clkdco, ctx->req_pck_min,
|
2017-08-05 01:44:15 +03:00
|
|
|
dsi->data->max_fck_freq,
|
2013-03-05 17:21:35 +02:00
|
|
|
dsi_vm_calc_hsdiv_cb, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dsi_vm_calc(struct dsi_data *dsi,
|
|
|
|
const struct omap_dss_dsi_config *cfg,
|
|
|
|
struct dsi_clk_calc_ctx *ctx)
|
|
|
|
{
|
2016-09-22 14:07:04 +03:00
|
|
|
const struct videomode *vm = cfg->vm;
|
2013-03-05 17:21:35 +02:00
|
|
|
unsigned long clkin;
|
|
|
|
unsigned long pll_min;
|
|
|
|
unsigned long pll_max;
|
|
|
|
int ndl = dsi->num_lanes_used - 1;
|
2020-12-15 12:45:36 +02:00
|
|
|
int bitspp = mipi_dsi_pixel_format_to_bpp(cfg->pixel_format);
|
2013-03-05 17:21:35 +02:00
|
|
|
unsigned long byteclk_min;
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
clkin = clk_get_rate(dsi->pll.clkin);
|
2013-03-05 17:21:35 +02:00
|
|
|
|
|
|
|
memset(ctx, 0, sizeof(*ctx));
|
2018-02-13 14:00:33 +02:00
|
|
|
ctx->dsi = dsi;
|
2014-10-22 14:49:14 +03:00
|
|
|
ctx->pll = &dsi->pll;
|
2013-03-05 17:21:35 +02:00
|
|
|
ctx->config = cfg;
|
|
|
|
|
|
|
|
/* these limits should come from the panel driver */
|
2016-09-22 14:07:04 +03:00
|
|
|
ctx->req_pck_min = vm->pixelclock - 1000;
|
|
|
|
ctx->req_pck_nom = vm->pixelclock;
|
|
|
|
ctx->req_pck_max = vm->pixelclock + 1000;
|
2013-03-05 17:21:35 +02:00
|
|
|
|
|
|
|
byteclk_min = div64_u64((u64)ctx->req_pck_min * bitspp, ndl * 8);
|
|
|
|
pll_min = max(cfg->hs_clk_min * 4, byteclk_min * 4 * 4);
|
|
|
|
|
|
|
|
if (cfg->trans_mode == OMAP_DSS_DSI_BURST_MODE) {
|
|
|
|
pll_max = cfg->hs_clk_max * 4;
|
|
|
|
} else {
|
|
|
|
unsigned long byteclk_max;
|
|
|
|
byteclk_max = div64_u64((u64)ctx->req_pck_max * bitspp,
|
|
|
|
ndl * 8);
|
|
|
|
|
|
|
|
pll_max = byteclk_max * 4 * 4;
|
|
|
|
}
|
|
|
|
|
2016-05-17 21:23:37 +03:00
|
|
|
return dss_pll_calc_a(ctx->pll, clkin,
|
2013-03-05 17:21:35 +02:00
|
|
|
pll_min, pll_max,
|
|
|
|
dsi_vm_calc_pll_cb, ctx);
|
2012-08-16 18:02:00 +05:30
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:04 +02:00
|
|
|
static bool dsi_is_video_mode(struct omap_dss_device *dssdev)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
|
|
|
|
|
|
|
return dsi->mode == OMAP_DSS_DSI_VIDEO_MODE;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:07 +02:00
|
|
|
static int __dsi_calc_config(struct dsi_data *dsi,
|
|
|
|
const struct drm_display_mode *mode,
|
|
|
|
struct dsi_clk_calc_ctx *ctx)
|
2012-08-13 22:13:39 +05:30
|
|
|
{
|
2020-12-15 12:46:05 +02:00
|
|
|
struct omap_dss_dsi_config cfg = dsi->config;
|
2020-12-15 12:46:07 +02:00
|
|
|
struct videomode vm;
|
2013-03-05 17:21:35 +02:00
|
|
|
bool ok;
|
|
|
|
int r;
|
2012-08-13 22:13:39 +05:30
|
|
|
|
2020-12-15 12:46:05 +02:00
|
|
|
drm_display_mode_to_videomode(mode, &vm);
|
2012-08-13 22:13:39 +05:30
|
|
|
|
2020-12-15 12:46:07 +02:00
|
|
|
cfg.vm = &vm;
|
2020-12-15 12:45:54 +02:00
|
|
|
cfg.mode = dsi->mode;
|
|
|
|
cfg.pixel_format = dsi->pix_fmt;
|
2012-08-09 15:23:43 +05:30
|
|
|
|
2020-12-15 12:45:54 +02:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE)
|
2020-12-15 12:46:07 +02:00
|
|
|
ok = dsi_vm_calc(dsi, &cfg, ctx);
|
2013-03-05 17:21:35 +02:00
|
|
|
else
|
2020-12-15 12:46:07 +02:00
|
|
|
ok = dsi_cm_calc(dsi, &cfg, ctx);
|
2013-03-05 17:21:35 +02:00
|
|
|
|
2020-12-15 12:46:07 +02:00
|
|
|
if (!ok)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dsi_pll_calc_dsi_fck(dsi, &ctx->dsi_cinfo);
|
2013-03-05 17:21:35 +02:00
|
|
|
|
2020-12-15 12:46:07 +02:00
|
|
|
r = dsi_lp_clock_calc(ctx->dsi_cinfo.clkout[HSDIV_DSI],
|
|
|
|
cfg.lp_clk_min, cfg.lp_clk_max, &ctx->lp_cinfo);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-03-05 17:21:35 +02:00
|
|
|
|
2020-12-15 12:46:07 +02:00
|
|
|
static int dsi_set_config(struct omap_dss_device *dssdev,
|
|
|
|
const struct drm_display_mode *mode)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = to_dsi_data(dssdev);
|
|
|
|
struct dsi_clk_calc_ctx ctx;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
mutex_lock(&dsi->lock);
|
|
|
|
|
|
|
|
r = __dsi_calc_config(dsi, mode, &ctx);
|
2013-03-05 17:21:35 +02:00
|
|
|
if (r) {
|
2020-12-15 12:46:07 +02:00
|
|
|
DSSERR("failed to find suitable DSI clock settings\n");
|
2013-03-05 17:21:35 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:07 +02:00
|
|
|
dsi->user_lp_cinfo = ctx.lp_cinfo;
|
2013-03-05 17:21:35 +02:00
|
|
|
dsi->user_dsi_cinfo = ctx.dsi_cinfo;
|
|
|
|
dsi->user_dispc_cinfo = ctx.dispc_cinfo;
|
|
|
|
|
2016-09-22 14:07:04 +03:00
|
|
|
dsi->vm = ctx.vm;
|
2018-06-08 15:59:31 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* override interlace, logic level and edge related parameters in
|
|
|
|
* videomode with default values
|
|
|
|
*/
|
|
|
|
dsi->vm.flags &= ~DISPLAY_FLAGS_INTERLACED;
|
|
|
|
dsi->vm.flags &= ~DISPLAY_FLAGS_HSYNC_LOW;
|
|
|
|
dsi->vm.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
|
|
|
|
dsi->vm.flags &= ~DISPLAY_FLAGS_VSYNC_LOW;
|
|
|
|
dsi->vm.flags |= DISPLAY_FLAGS_VSYNC_HIGH;
|
2019-01-11 05:50:35 +02:00
|
|
|
/*
|
|
|
|
* HACK: These flags should be handled through the omap_dss_device bus
|
|
|
|
* flags, but this will only be possible when the DSI encoder will be
|
|
|
|
* converted to the omapdrm-managed encoder model.
|
|
|
|
*/
|
|
|
|
dsi->vm.flags &= ~DISPLAY_FLAGS_PIXDATA_NEGEDGE;
|
|
|
|
dsi->vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
|
|
|
|
dsi->vm.flags &= ~DISPLAY_FLAGS_DE_LOW;
|
|
|
|
dsi->vm.flags |= DISPLAY_FLAGS_DE_HIGH;
|
|
|
|
dsi->vm.flags &= ~DISPLAY_FLAGS_SYNC_POSEDGE;
|
|
|
|
dsi->vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
|
2018-06-08 15:59:31 +03:00
|
|
|
|
2018-06-08 16:53:37 +03:00
|
|
|
dss_mgr_set_timings(&dsi->output, &dsi->vm);
|
|
|
|
|
2013-03-05 17:21:35 +02:00
|
|
|
dsi->vm_timings = ctx.dsi_vm;
|
2012-08-13 22:13:39 +05:30
|
|
|
|
|
|
|
mutex_unlock(&dsi->lock);
|
2012-08-09 15:23:43 +05:30
|
|
|
|
2013-03-06 11:10:29 +02:00
|
|
|
return 0;
|
2013-03-05 17:21:35 +02:00
|
|
|
err:
|
|
|
|
mutex_unlock(&dsi->lock);
|
|
|
|
|
|
|
|
return r;
|
2012-08-13 22:13:39 +05:30
|
|
|
}
|
|
|
|
|
2013-02-13 11:23:54 +02:00
|
|
|
/*
|
2020-12-15 12:46:37 +02:00
|
|
|
* Return a hardcoded dispc channel for the DSI output. This should work for
|
2013-02-13 11:23:54 +02:00
|
|
|
* current use cases, but this can be later expanded to either resolve
|
|
|
|
* the channel in some more dynamic manner, or get the channel as a user
|
|
|
|
* parameter.
|
|
|
|
*/
|
2020-12-15 12:46:37 +02:00
|
|
|
static enum omap_channel dsi_get_dispc_channel(struct dsi_data *dsi)
|
2013-02-13 11:23:54 +02:00
|
|
|
{
|
2017-08-05 01:43:57 +03:00
|
|
|
switch (dsi->data->model) {
|
|
|
|
case DSI_MODEL_OMAP3:
|
2013-02-13 11:23:54 +02:00
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
|
2017-08-05 01:43:57 +03:00
|
|
|
case DSI_MODEL_OMAP4:
|
|
|
|
switch (dsi->module_id) {
|
2013-02-13 11:23:54 +02:00
|
|
|
case 0:
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
case 1:
|
|
|
|
return OMAP_DSS_CHANNEL_LCD2;
|
|
|
|
default:
|
|
|
|
DSSWARN("unsupported module id\n");
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
}
|
|
|
|
|
2017-08-05 01:43:57 +03:00
|
|
|
case DSI_MODEL_OMAP5:
|
|
|
|
switch (dsi->module_id) {
|
2013-02-13 11:23:54 +02:00
|
|
|
case 0:
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
case 1:
|
|
|
|
return OMAP_DSS_CHANNEL_LCD3;
|
|
|
|
default:
|
|
|
|
DSSWARN("unsupported module id\n");
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
DSSWARN("unsupported DSS version\n");
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
}
|
2012-08-13 22:13:39 +05:30
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:39 +02:00
|
|
|
static ssize_t _omap_dsi_host_transfer(struct dsi_data *dsi, int vc,
|
2020-12-15 12:45:59 +02:00
|
|
|
const struct mipi_dsi_msg *msg)
|
2020-12-15 12:45:38 +02:00
|
|
|
{
|
2020-12-15 12:45:45 +02:00
|
|
|
struct omap_dss_device *dssdev = &dsi->output;
|
2020-12-15 12:45:58 +02:00
|
|
|
int r;
|
2020-12-15 12:45:45 +02:00
|
|
|
|
2020-12-15 12:46:41 +02:00
|
|
|
dsi_vc_enable_hs(dssdev, vc, !(msg->flags & MIPI_DSI_MSG_USE_LPM));
|
2020-12-15 12:45:56 +02:00
|
|
|
|
2020-12-15 12:45:38 +02:00
|
|
|
switch (msg->type) {
|
|
|
|
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_LONG_WRITE:
|
|
|
|
case MIPI_DSI_DCS_SHORT_WRITE:
|
|
|
|
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
|
|
|
|
case MIPI_DSI_DCS_LONG_WRITE:
|
2020-12-15 12:45:42 +02:00
|
|
|
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
|
|
|
|
case MIPI_DSI_NULL_PACKET:
|
2020-12-15 12:46:39 +02:00
|
|
|
r = dsi_vc_write_common(dssdev, vc, msg);
|
2020-12-15 12:45:58 +02:00
|
|
|
break;
|
2020-12-15 12:45:38 +02:00
|
|
|
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
|
|
|
|
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
|
2020-12-15 12:46:39 +02:00
|
|
|
r = dsi_vc_generic_read(dssdev, vc, msg);
|
2020-12-15 12:45:58 +02:00
|
|
|
break;
|
2020-12-15 12:45:38 +02:00
|
|
|
case MIPI_DSI_DCS_READ:
|
2020-12-15 12:46:39 +02:00
|
|
|
r = dsi_vc_dcs_read(dssdev, vc, msg);
|
2020-12-15 12:45:58 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
r = -EINVAL;
|
|
|
|
break;
|
2020-12-15 12:45:38 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:58 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (msg->type == MIPI_DSI_DCS_SHORT_WRITE ||
|
|
|
|
msg->type == MIPI_DSI_DCS_SHORT_WRITE_PARAM) {
|
|
|
|
u8 cmd = ((u8 *)msg->tx_buf)[0];
|
|
|
|
|
|
|
|
if (cmd == MIPI_DCS_SET_TEAR_OFF)
|
|
|
|
dsi_enable_te(dsi, false);
|
|
|
|
else if (cmd == MIPI_DCS_SET_TEAR_ON)
|
|
|
|
dsi_enable_te(dsi, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-12-15 12:45:38 +02:00
|
|
|
}
|
2010-06-09 15:28:12 +03:00
|
|
|
|
2020-12-15 12:45:59 +02:00
|
|
|
static ssize_t omap_dsi_host_transfer(struct mipi_dsi_host *host,
|
|
|
|
const struct mipi_dsi_msg *msg)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = host_to_omap(host);
|
|
|
|
int r;
|
2020-12-15 12:46:43 +02:00
|
|
|
int vc = VC_CMD;
|
2020-12-15 12:45:59 +02:00
|
|
|
|
|
|
|
dsi_bus_lock(dsi);
|
2020-12-15 12:46:05 +02:00
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
if (!dsi->iface_enabled) {
|
|
|
|
dsi_enable(dsi);
|
|
|
|
schedule_delayed_work(&dsi->dsi_disable_work, msecs_to_jiffies(2000));
|
|
|
|
}
|
|
|
|
|
|
|
|
r = _omap_dsi_host_transfer(dsi, vc, msg);
|
2020-12-15 12:46:05 +02:00
|
|
|
|
2020-12-15 12:45:59 +02:00
|
|
|
dsi_bus_unlock(dsi);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_get_clocks(struct dsi_data *dsi)
|
2011-05-27 10:52:19 +03:00
|
|
|
{
|
|
|
|
struct clk *clk;
|
|
|
|
|
2018-02-13 14:00:36 +02:00
|
|
|
clk = devm_clk_get(dsi->dev, "fck");
|
2011-05-27 10:52:19 +03:00
|
|
|
if (IS_ERR(clk)) {
|
|
|
|
DSSERR("can't get fck\n");
|
|
|
|
return PTR_ERR(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi->dss_clk = clk;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:20 +02:00
|
|
|
static const struct omapdss_dsi_ops dsi_ops = {
|
|
|
|
.update = dsi_update_all,
|
|
|
|
.is_video_mode = dsi_is_video_mode,
|
2013-05-24 13:20:27 +03:00
|
|
|
};
|
|
|
|
|
2020-12-15 12:45:57 +02:00
|
|
|
static irqreturn_t omap_dsi_te_irq_handler(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = (struct dsi_data *)dev_id;
|
|
|
|
int old;
|
|
|
|
|
|
|
|
old = atomic_cmpxchg(&dsi->do_ext_te_update, 1, 0);
|
|
|
|
if (old) {
|
|
|
|
cancel_delayed_work(&dsi->te_timeout_work);
|
|
|
|
_dsi_update(dsi);
|
|
|
|
}
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_dsi_te_timeout_work_callback(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi =
|
|
|
|
container_of(work, struct dsi_data, te_timeout_work.work);
|
|
|
|
int old;
|
|
|
|
|
|
|
|
old = atomic_cmpxchg(&dsi->do_ext_te_update, 1, 0);
|
|
|
|
if (old) {
|
|
|
|
dev_err(dsi->dev, "TE not received for 250ms!\n");
|
|
|
|
_dsi_update(dsi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap_dsi_register_te_irq(struct dsi_data *dsi,
|
|
|
|
struct mipi_dsi_device *client)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
int te_irq;
|
|
|
|
|
|
|
|
dsi->te_gpio = gpiod_get(&client->dev, "te-gpios", GPIOD_IN);
|
|
|
|
if (IS_ERR(dsi->te_gpio)) {
|
|
|
|
err = PTR_ERR(dsi->te_gpio);
|
|
|
|
|
|
|
|
if (err == -ENOENT) {
|
|
|
|
dsi->te_gpio = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_err(dsi->dev, "Could not get TE gpio: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
te_irq = gpiod_to_irq(dsi->te_gpio);
|
|
|
|
if (te_irq < 0) {
|
|
|
|
gpiod_put(dsi->te_gpio);
|
|
|
|
dsi->te_gpio = NULL;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi->te_irq = te_irq;
|
|
|
|
|
|
|
|
irq_set_status_flags(te_irq, IRQ_NOAUTOEN);
|
|
|
|
|
|
|
|
err = request_threaded_irq(te_irq, NULL, omap_dsi_te_irq_handler,
|
2021-03-23 17:34:53 +08:00
|
|
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
|
|
|
"TE", dsi);
|
2020-12-15 12:45:57 +02:00
|
|
|
if (err) {
|
|
|
|
dev_err(dsi->dev, "request irq failed with %d\n", err);
|
|
|
|
gpiod_put(dsi->te_gpio);
|
|
|
|
dsi->te_gpio = NULL;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
INIT_DEFERRABLE_WORK(&dsi->te_timeout_work,
|
|
|
|
omap_dsi_te_timeout_work_callback);
|
|
|
|
|
|
|
|
dev_dbg(dsi->dev, "Using GPIO TE\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_dsi_unregister_te_irq(struct dsi_data *dsi)
|
|
|
|
{
|
|
|
|
if (dsi->te_gpio) {
|
|
|
|
free_irq(dsi->te_irq, dsi);
|
|
|
|
cancel_delayed_work(&dsi->te_timeout_work);
|
|
|
|
gpiod_put(dsi->te_gpio);
|
|
|
|
dsi->te_gpio = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:45 +02:00
|
|
|
static int omap_dsi_host_attach(struct mipi_dsi_host *host,
|
2020-12-15 12:45:47 +02:00
|
|
|
struct mipi_dsi_device *client)
|
2020-12-15 12:45:45 +02:00
|
|
|
{
|
2020-12-15 12:45:47 +02:00
|
|
|
struct dsi_data *dsi = host_to_omap(host);
|
2020-12-15 12:45:57 +02:00
|
|
|
int r;
|
2020-12-15 12:45:47 +02:00
|
|
|
|
2020-12-15 12:46:35 +02:00
|
|
|
if (dsi->dsidev) {
|
|
|
|
DSSERR("dsi client already attached\n");
|
2020-12-15 12:45:47 +02:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:54 +02:00
|
|
|
if (mipi_dsi_pixel_format_to_bpp(client->format) < 0) {
|
|
|
|
DSSERR("invalid pixel format\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:57 +02:00
|
|
|
atomic_set(&dsi->do_ext_te_update, 0);
|
2020-12-15 12:45:54 +02:00
|
|
|
|
2020-12-15 12:45:57 +02:00
|
|
|
if (client->mode_flags & MIPI_DSI_MODE_VIDEO) {
|
2020-12-15 12:45:54 +02:00
|
|
|
dsi->mode = OMAP_DSS_DSI_VIDEO_MODE;
|
2020-12-15 12:45:57 +02:00
|
|
|
} else {
|
|
|
|
r = omap_dsi_register_te_irq(dsi, client);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
2020-12-15 12:45:54 +02:00
|
|
|
dsi->mode = OMAP_DSS_DSI_CMD_MODE;
|
2020-12-15 12:45:57 +02:00
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:35 +02:00
|
|
|
dsi->dsidev = client;
|
2020-12-15 12:45:57 +02:00
|
|
|
dsi->pix_fmt = client->format;
|
2020-12-15 12:45:54 +02:00
|
|
|
|
2020-12-15 12:46:05 +02:00
|
|
|
dsi->config.hs_clk_min = 150000000; // TODO: get from client?
|
|
|
|
dsi->config.hs_clk_max = client->hs_rate;
|
|
|
|
dsi->config.lp_clk_min = 7000000; // TODO: get from client?
|
|
|
|
dsi->config.lp_clk_max = client->lp_rate;
|
|
|
|
|
2020-12-15 12:46:32 +02:00
|
|
|
if (client->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
|
|
|
|
dsi->config.trans_mode = OMAP_DSS_DSI_BURST_MODE;
|
|
|
|
else if (client->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
|
|
|
|
dsi->config.trans_mode = OMAP_DSS_DSI_PULSE_MODE;
|
|
|
|
else
|
|
|
|
dsi->config.trans_mode = OMAP_DSS_DSI_EVENT_MODE;
|
|
|
|
|
2020-12-15 12:45:45 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap_dsi_host_detach(struct mipi_dsi_host *host,
|
2020-12-15 12:45:47 +02:00
|
|
|
struct mipi_dsi_device *client)
|
2020-12-15 12:45:45 +02:00
|
|
|
{
|
2020-12-15 12:45:47 +02:00
|
|
|
struct dsi_data *dsi = host_to_omap(host);
|
|
|
|
|
2020-12-15 12:46:35 +02:00
|
|
|
if (WARN_ON(dsi->dsidev != client))
|
2020-12-15 12:45:47 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
cancel_delayed_work_sync(&dsi->dsi_disable_work);
|
|
|
|
|
|
|
|
dsi_bus_lock(dsi);
|
|
|
|
|
|
|
|
if (dsi->iface_enabled)
|
|
|
|
dsi_disable(dsi);
|
|
|
|
|
|
|
|
dsi_bus_unlock(dsi);
|
|
|
|
|
2020-12-15 12:45:57 +02:00
|
|
|
omap_dsi_unregister_te_irq(dsi);
|
2020-12-15 12:46:35 +02:00
|
|
|
dsi->dsidev = NULL;
|
2020-12-15 12:45:45 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct mipi_dsi_host_ops omap_dsi_host_ops = {
|
|
|
|
.attach = omap_dsi_host_attach,
|
|
|
|
.detach = omap_dsi_host_detach,
|
|
|
|
.transfer = omap_dsi_host_transfer,
|
|
|
|
};
|
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
/* -----------------------------------------------------------------------------
|
|
|
|
* PLL
|
|
|
|
*/
|
2012-08-21 15:35:42 +03:00
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
static const struct dss_pll_ops dsi_pll_ops = {
|
|
|
|
.enable = dsi_pll_enable,
|
|
|
|
.disable = dsi_pll_disable,
|
|
|
|
.set_config = dss_pll_write_config_type_a,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dss_pll_hw dss_omap3_dsi_pll_hw = {
|
2016-05-18 10:48:44 +03:00
|
|
|
.type = DSS_PLL_TYPE_A,
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
.n_max = (1 << 7) - 1,
|
|
|
|
.m_max = (1 << 11) - 1,
|
|
|
|
.mX_max = (1 << 4) - 1,
|
|
|
|
.fint_min = 750000,
|
|
|
|
.fint_max = 2100000,
|
|
|
|
.clkdco_low = 1000000000,
|
|
|
|
.clkdco_max = 1800000000,
|
|
|
|
|
|
|
|
.n_msb = 7,
|
|
|
|
.n_lsb = 1,
|
|
|
|
.m_msb = 18,
|
|
|
|
.m_lsb = 8,
|
|
|
|
|
|
|
|
.mX_msb[0] = 22,
|
|
|
|
.mX_lsb[0] = 19,
|
|
|
|
.mX_msb[1] = 26,
|
|
|
|
.mX_lsb[1] = 23,
|
|
|
|
|
|
|
|
.has_stopmode = true,
|
|
|
|
.has_freqsel = true,
|
|
|
|
.has_selfreqdco = false,
|
|
|
|
.has_refsel = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dss_pll_hw dss_omap4_dsi_pll_hw = {
|
2016-05-18 10:48:44 +03:00
|
|
|
.type = DSS_PLL_TYPE_A,
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
.n_max = (1 << 8) - 1,
|
|
|
|
.m_max = (1 << 12) - 1,
|
|
|
|
.mX_max = (1 << 5) - 1,
|
|
|
|
.fint_min = 500000,
|
|
|
|
.fint_max = 2500000,
|
|
|
|
.clkdco_low = 1000000000,
|
|
|
|
.clkdco_max = 1800000000,
|
|
|
|
|
|
|
|
.n_msb = 8,
|
|
|
|
.n_lsb = 1,
|
|
|
|
.m_msb = 20,
|
|
|
|
.m_lsb = 9,
|
|
|
|
|
|
|
|
.mX_msb[0] = 25,
|
|
|
|
.mX_lsb[0] = 21,
|
|
|
|
.mX_msb[1] = 30,
|
|
|
|
.mX_lsb[1] = 26,
|
|
|
|
|
|
|
|
.has_stopmode = true,
|
|
|
|
.has_freqsel = false,
|
|
|
|
.has_selfreqdco = false,
|
|
|
|
.has_refsel = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dss_pll_hw dss_omap5_dsi_pll_hw = {
|
2016-05-18 10:48:44 +03:00
|
|
|
.type = DSS_PLL_TYPE_A,
|
|
|
|
|
2014-10-22 14:49:14 +03:00
|
|
|
.n_max = (1 << 8) - 1,
|
|
|
|
.m_max = (1 << 12) - 1,
|
|
|
|
.mX_max = (1 << 5) - 1,
|
|
|
|
.fint_min = 150000,
|
|
|
|
.fint_max = 52000000,
|
|
|
|
.clkdco_low = 1000000000,
|
|
|
|
.clkdco_max = 1800000000,
|
|
|
|
|
|
|
|
.n_msb = 8,
|
|
|
|
.n_lsb = 1,
|
|
|
|
.m_msb = 20,
|
|
|
|
.m_lsb = 9,
|
|
|
|
|
|
|
|
.mX_msb[0] = 25,
|
|
|
|
.mX_lsb[0] = 21,
|
|
|
|
.mX_msb[1] = 30,
|
|
|
|
.mX_lsb[1] = 26,
|
|
|
|
|
|
|
|
.has_stopmode = true,
|
|
|
|
.has_freqsel = false,
|
|
|
|
.has_selfreqdco = true,
|
|
|
|
.has_refsel = true,
|
|
|
|
};
|
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
static int dsi_init_pll_data(struct dss_device *dss, struct dsi_data *dsi)
|
2014-10-22 14:49:14 +03:00
|
|
|
{
|
|
|
|
struct dss_pll *pll = &dsi->pll;
|
|
|
|
struct clk *clk;
|
|
|
|
int r;
|
|
|
|
|
2018-02-13 14:00:36 +02:00
|
|
|
clk = devm_clk_get(dsi->dev, "sys_clk");
|
2014-10-22 14:49:14 +03:00
|
|
|
if (IS_ERR(clk)) {
|
|
|
|
DSSERR("can't get sys_clk\n");
|
|
|
|
return PTR_ERR(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
pll->name = dsi->module_id == 0 ? "dsi0" : "dsi1";
|
2015-01-02 10:05:33 +02:00
|
|
|
pll->id = dsi->module_id == 0 ? DSS_PLL_DSI1 : DSS_PLL_DSI2;
|
2014-10-22 14:49:14 +03:00
|
|
|
pll->clkin = clk;
|
|
|
|
pll->base = dsi->pll_base;
|
2017-08-05 01:43:57 +03:00
|
|
|
pll->hw = dsi->data->pll_hw;
|
2014-10-22 14:49:14 +03:00
|
|
|
pll->ops = &dsi_pll_ops;
|
|
|
|
|
2018-02-13 14:00:30 +02:00
|
|
|
r = dss_pll_register(dss, pll);
|
2014-10-22 14:49:14 +03:00
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
/* -----------------------------------------------------------------------------
|
|
|
|
* Component Bind & Unbind
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int dsi_bind(struct device *dev, struct device *master, void *data)
|
|
|
|
{
|
|
|
|
struct dss_device *dss = dss_get_device(master);
|
|
|
|
struct dsi_data *dsi = dev_get_drvdata(dev);
|
|
|
|
char name[10];
|
|
|
|
u32 rev;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
dsi->dss = dss;
|
|
|
|
|
|
|
|
dsi_init_pll_data(dss, dsi);
|
|
|
|
|
|
|
|
r = dsi_runtime_get(dsi);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
rev = dsi_read_reg(dsi, DSI_REVISION);
|
|
|
|
dev_dbg(dev, "OMAP DSI rev %d.%d\n",
|
|
|
|
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
|
|
|
|
|
|
|
|
dsi->line_buffer_size = dsi_get_line_buf_size(dsi);
|
|
|
|
|
|
|
|
dsi_runtime_put(dsi);
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "dsi%u_regs", dsi->module_id + 1);
|
|
|
|
dsi->debugfs.regs = dss_debugfs_create_file(dss, name,
|
2019-01-11 05:50:33 +02:00
|
|
|
dsi_dump_dsi_regs, dsi);
|
2018-03-03 18:52:59 +02:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
|
|
|
|
snprintf(name, sizeof(name), "dsi%u_irqs", dsi->module_id + 1);
|
|
|
|
dsi->debugfs.irqs = dss_debugfs_create_file(dss, name,
|
2019-01-11 05:50:33 +02:00
|
|
|
dsi_dump_dsi_irqs, dsi);
|
2018-03-03 18:52:59 +02:00
|
|
|
#endif
|
|
|
|
snprintf(name, sizeof(name), "dsi%u_clks", dsi->module_id + 1);
|
|
|
|
dsi->debugfs.clks = dss_debugfs_create_file(dss, name,
|
2019-01-11 05:50:33 +02:00
|
|
|
dsi_dump_dsi_clocks, dsi);
|
2018-03-03 18:52:59 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_unbind(struct device *dev, struct device *master, void *data)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
dss_debugfs_remove_file(dsi->debugfs.clks);
|
|
|
|
dss_debugfs_remove_file(dsi->debugfs.irqs);
|
|
|
|
dss_debugfs_remove_file(dsi->debugfs.regs);
|
|
|
|
|
|
|
|
WARN_ON(dsi->scp_clk_refcount > 0);
|
|
|
|
|
|
|
|
dss_pll_unregister(&dsi->pll);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct component_ops dsi_component_ops = {
|
|
|
|
.bind = dsi_bind,
|
|
|
|
.unbind = dsi_unbind,
|
|
|
|
};
|
|
|
|
|
2020-12-15 12:46:13 +02:00
|
|
|
/* -----------------------------------------------------------------------------
|
|
|
|
* DRM Bridge Operations
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int dsi_bridge_attach(struct drm_bridge *bridge,
|
|
|
|
enum drm_bridge_attach_flags flags)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
|
|
|
|
|
|
|
|
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return drm_bridge_attach(bridge->encoder, dsi->output.next_bridge,
|
|
|
|
bridge, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum drm_mode_status
|
|
|
|
dsi_bridge_mode_valid(struct drm_bridge *bridge,
|
|
|
|
const struct drm_display_info *info,
|
|
|
|
const struct drm_display_mode *mode)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
|
|
|
|
struct dsi_clk_calc_ctx ctx;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
mutex_lock(&dsi->lock);
|
|
|
|
r = __dsi_calc_config(dsi, mode, &ctx);
|
|
|
|
mutex_unlock(&dsi->lock);
|
|
|
|
|
|
|
|
return r ? MODE_CLOCK_RANGE : MODE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_bridge_mode_set(struct drm_bridge *bridge,
|
|
|
|
const struct drm_display_mode *mode,
|
|
|
|
const struct drm_display_mode *adjusted_mode)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
|
|
|
|
|
|
|
|
dsi_set_config(&dsi->output, adjusted_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_bridge_enable(struct drm_bridge *bridge)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
|
2020-12-15 12:46:48 +02:00
|
|
|
struct omap_dss_device *dssdev = &dsi->output;
|
2020-12-15 12:46:13 +02:00
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
cancel_delayed_work_sync(&dsi->dsi_disable_work);
|
|
|
|
|
2020-12-15 12:46:48 +02:00
|
|
|
dsi_bus_lock(dsi);
|
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
if (!dsi->iface_enabled)
|
|
|
|
dsi_enable(dsi);
|
2020-12-15 12:46:48 +02:00
|
|
|
|
|
|
|
dsi_enable_video_output(dssdev, VC_VIDEO);
|
|
|
|
|
|
|
|
dsi->video_enabled = true;
|
|
|
|
|
|
|
|
dsi_bus_unlock(dsi);
|
2020-12-15 12:46:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_bridge_disable(struct drm_bridge *bridge)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
|
2020-12-15 12:46:48 +02:00
|
|
|
struct omap_dss_device *dssdev = &dsi->output;
|
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
cancel_delayed_work_sync(&dsi->dsi_disable_work);
|
|
|
|
|
2020-12-15 12:46:48 +02:00
|
|
|
dsi_bus_lock(dsi);
|
|
|
|
|
|
|
|
dsi->video_enabled = false;
|
|
|
|
|
|
|
|
dsi_disable_video_output(dssdev, VC_VIDEO);
|
2020-12-15 12:46:13 +02:00
|
|
|
|
2020-12-15 12:46:55 +02:00
|
|
|
dsi_disable(dsi);
|
2020-12-15 12:46:48 +02:00
|
|
|
|
|
|
|
dsi_bus_unlock(dsi);
|
2020-12-15 12:46:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_bridge_funcs dsi_bridge_funcs = {
|
|
|
|
.attach = dsi_bridge_attach,
|
|
|
|
.mode_valid = dsi_bridge_mode_valid,
|
|
|
|
.mode_set = dsi_bridge_mode_set,
|
|
|
|
.enable = dsi_bridge_enable,
|
|
|
|
.disable = dsi_bridge_disable,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void dsi_bridge_init(struct dsi_data *dsi)
|
|
|
|
{
|
|
|
|
dsi->bridge.funcs = &dsi_bridge_funcs;
|
|
|
|
dsi->bridge.of_node = dsi->host.dev->of_node;
|
|
|
|
dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
|
|
|
|
|
|
|
|
drm_bridge_add(&dsi->bridge);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_bridge_cleanup(struct dsi_data *dsi)
|
|
|
|
{
|
|
|
|
drm_bridge_remove(&dsi->bridge);
|
|
|
|
}
|
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
/* -----------------------------------------------------------------------------
|
|
|
|
* Probe & Remove, Suspend & Resume
|
|
|
|
*/
|
|
|
|
|
2018-03-02 22:13:06 +02:00
|
|
|
static int dsi_init_output(struct dsi_data *dsi)
|
2018-03-03 18:52:59 +02:00
|
|
|
{
|
|
|
|
struct omap_dss_device *out = &dsi->output;
|
2018-03-06 01:25:13 +02:00
|
|
|
int r;
|
2018-03-03 18:52:59 +02:00
|
|
|
|
2020-12-15 12:46:13 +02:00
|
|
|
dsi_bridge_init(dsi);
|
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
out->dev = dsi->dev;
|
|
|
|
out->id = dsi->module_id == 0 ?
|
|
|
|
OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2;
|
|
|
|
|
2018-12-10 14:00:38 +02:00
|
|
|
out->type = OMAP_DISPLAY_TYPE_DSI;
|
2018-03-03 18:52:59 +02:00
|
|
|
out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1";
|
2020-12-15 12:46:37 +02:00
|
|
|
out->dispc_channel = dsi_get_dispc_channel(dsi);
|
2020-12-15 12:46:20 +02:00
|
|
|
out->dsi_ops = &dsi_ops;
|
2020-02-26 13:24:45 +02:00
|
|
|
out->of_port = 0;
|
2018-09-22 15:02:42 +03:00
|
|
|
out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE
|
2018-06-06 15:20:01 +03:00
|
|
|
| DRM_BUS_FLAG_DE_HIGH
|
2018-09-22 15:02:42 +03:00
|
|
|
| DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE;
|
2018-03-03 18:52:59 +02:00
|
|
|
|
2020-12-15 12:46:13 +02:00
|
|
|
r = omapdss_device_init_output(out, &dsi->bridge);
|
|
|
|
if (r < 0) {
|
|
|
|
dsi_bridge_cleanup(dsi);
|
2018-03-06 01:25:13 +02:00
|
|
|
return r;
|
2020-12-15 12:46:13 +02:00
|
|
|
}
|
2018-03-06 01:25:13 +02:00
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
omapdss_device_register(out);
|
2018-03-02 22:13:06 +02:00
|
|
|
|
|
|
|
return 0;
|
2018-03-03 18:52:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_uninit_output(struct dsi_data *dsi)
|
|
|
|
{
|
|
|
|
struct omap_dss_device *out = &dsi->output;
|
|
|
|
|
|
|
|
omapdss_device_unregister(out);
|
2018-09-12 19:41:31 +03:00
|
|
|
omapdss_device_cleanup_output(out);
|
2020-12-15 12:46:13 +02:00
|
|
|
dsi_bridge_cleanup(dsi);
|
2018-03-03 18:52:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dsi_probe_of(struct dsi_data *dsi)
|
|
|
|
{
|
|
|
|
struct device_node *node = dsi->dev->of_node;
|
|
|
|
struct property *prop;
|
|
|
|
u32 lane_arr[10];
|
|
|
|
int len, num_pins;
|
2020-12-15 12:46:21 +02:00
|
|
|
int r;
|
2018-03-03 18:52:59 +02:00
|
|
|
struct device_node *ep;
|
|
|
|
|
|
|
|
ep = of_graph_get_endpoint_by_regs(node, 0, 0);
|
|
|
|
if (!ep)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
prop = of_find_property(ep, "lanes", &len);
|
|
|
|
if (prop == NULL) {
|
|
|
|
dev_err(dsi->dev, "failed to find lane data\n");
|
|
|
|
r = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_pins = len / sizeof(u32);
|
|
|
|
|
|
|
|
if (num_pins < 4 || num_pins % 2 != 0 ||
|
|
|
|
num_pins > dsi->num_lanes_supported * 2) {
|
|
|
|
dev_err(dsi->dev, "bad number of lanes\n");
|
|
|
|
r = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = of_property_read_u32_array(ep, "lanes", lane_arr, num_pins);
|
|
|
|
if (r) {
|
|
|
|
dev_err(dsi->dev, "failed to read lane data\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:46:21 +02:00
|
|
|
r = dsi_configure_pins(dsi, num_pins, lane_arr);
|
2018-03-03 18:52:59 +02:00
|
|
|
if (r) {
|
|
|
|
dev_err(dsi->dev, "failed to configure pins");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
of_node_put(ep);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
of_node_put(ep);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2017-08-05 01:44:10 +03:00
|
|
|
static const struct dsi_of_data dsi_of_data_omap34xx = {
|
|
|
|
.model = DSI_MODEL_OMAP3,
|
|
|
|
.pll_hw = &dss_omap3_dsi_pll_hw,
|
|
|
|
.modules = (const struct dsi_module_id_data[]) {
|
|
|
|
{ .address = 0x4804fc00, .id = 0, },
|
|
|
|
{ },
|
|
|
|
},
|
2017-08-05 01:44:15 +03:00
|
|
|
.max_fck_freq = 173000000,
|
|
|
|
.max_pll_lpdiv = (1 << 13) - 1,
|
2017-08-05 01:44:10 +03:00
|
|
|
.quirks = DSI_QUIRK_REVERSE_TXCLKESC,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dsi_of_data dsi_of_data_omap36xx = {
|
|
|
|
.model = DSI_MODEL_OMAP3,
|
|
|
|
.pll_hw = &dss_omap3_dsi_pll_hw,
|
|
|
|
.modules = (const struct dsi_module_id_data[]) {
|
|
|
|
{ .address = 0x4804fc00, .id = 0, },
|
|
|
|
{ },
|
|
|
|
},
|
2017-08-05 01:44:15 +03:00
|
|
|
.max_fck_freq = 173000000,
|
|
|
|
.max_pll_lpdiv = (1 << 13) - 1,
|
2017-08-05 01:44:10 +03:00
|
|
|
.quirks = DSI_QUIRK_PLL_PWR_BUG,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dsi_of_data dsi_of_data_omap4 = {
|
|
|
|
.model = DSI_MODEL_OMAP4,
|
|
|
|
.pll_hw = &dss_omap4_dsi_pll_hw,
|
|
|
|
.modules = (const struct dsi_module_id_data[]) {
|
|
|
|
{ .address = 0x58004000, .id = 0, },
|
|
|
|
{ .address = 0x58005000, .id = 1, },
|
|
|
|
{ },
|
|
|
|
},
|
2017-08-05 01:44:15 +03:00
|
|
|
.max_fck_freq = 170000000,
|
|
|
|
.max_pll_lpdiv = (1 << 13) - 1,
|
2017-08-05 01:44:10 +03:00
|
|
|
.quirks = DSI_QUIRK_DCS_CMD_CONFIG_VC | DSI_QUIRK_VC_OCP_WIDTH
|
|
|
|
| DSI_QUIRK_GNQ,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dsi_of_data dsi_of_data_omap5 = {
|
|
|
|
.model = DSI_MODEL_OMAP5,
|
|
|
|
.pll_hw = &dss_omap5_dsi_pll_hw,
|
|
|
|
.modules = (const struct dsi_module_id_data[]) {
|
|
|
|
{ .address = 0x58004000, .id = 0, },
|
|
|
|
{ .address = 0x58009000, .id = 1, },
|
|
|
|
{ },
|
|
|
|
},
|
2017-08-05 01:44:15 +03:00
|
|
|
.max_fck_freq = 209250000,
|
|
|
|
.max_pll_lpdiv = (1 << 13) - 1,
|
2017-08-05 01:44:10 +03:00
|
|
|
.quirks = DSI_QUIRK_DCS_CMD_CONFIG_VC | DSI_QUIRK_VC_OCP_WIDTH
|
|
|
|
| DSI_QUIRK_GNQ | DSI_QUIRK_PHY_DCC,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct of_device_id dsi_of_match[] = {
|
|
|
|
{ .compatible = "ti,omap3-dsi", .data = &dsi_of_data_omap36xx, },
|
|
|
|
{ .compatible = "ti,omap4-dsi", .data = &dsi_of_data_omap4, },
|
|
|
|
{ .compatible = "ti,omap5-dsi", .data = &dsi_of_data_omap5, },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct soc_device_attribute dsi_soc_devices[] = {
|
|
|
|
{ .machine = "OMAP3[45]*", .data = &dsi_of_data_omap34xx },
|
|
|
|
{ .machine = "AM35*", .data = &dsi_of_data_omap34xx },
|
|
|
|
{ /* sentinel */ }
|
|
|
|
};
|
2018-02-13 14:00:35 +02:00
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
static void omap_dsi_disable_work_callback(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = container_of(work, struct dsi_data, dsi_disable_work.work);
|
|
|
|
|
|
|
|
dsi_bus_lock(dsi);
|
|
|
|
|
|
|
|
if (dsi->iface_enabled && !dsi->video_enabled)
|
|
|
|
dsi_disable(dsi);
|
|
|
|
|
|
|
|
dsi_bus_unlock(dsi);
|
|
|
|
}
|
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
static int dsi_probe(struct platform_device *pdev)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2017-08-05 01:44:10 +03:00
|
|
|
const struct soc_device_attribute *soc;
|
2017-05-07 00:42:26 +03:00
|
|
|
const struct dsi_module_id_data *d;
|
2018-03-03 18:52:59 +02:00
|
|
|
struct device *dev = &pdev->dev;
|
2011-05-12 17:26:27 +05:30
|
|
|
struct dsi_data *dsi;
|
2012-08-21 15:35:42 +03:00
|
|
|
struct resource *dsi_mem;
|
2013-12-17 13:53:28 +02:00
|
|
|
struct resource *res;
|
2018-03-03 18:52:59 +02:00
|
|
|
unsigned int i;
|
|
|
|
int r;
|
2011-05-12 17:26:27 +05:30
|
|
|
|
2018-02-13 14:00:35 +02:00
|
|
|
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
|
2012-01-25 13:31:04 +02:00
|
|
|
if (!dsi)
|
|
|
|
return -ENOMEM;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-02-13 14:00:36 +02:00
|
|
|
dsi->dev = dev;
|
2018-02-13 14:00:35 +02:00
|
|
|
dev_set_drvdata(dev, dsi);
|
2011-05-12 17:26:26 +05:30
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock_init(&dsi->irq_lock);
|
|
|
|
spin_lock_init(&dsi->errors_lock);
|
|
|
|
dsi->errors = 0;
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2009-12-17 14:35:21 +02:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
|
2011-05-12 17:26:27 +05:30
|
|
|
spin_lock_init(&dsi->irq_stats_lock);
|
|
|
|
dsi->irq_stats.last_reset = jiffies;
|
2009-12-17 14:35:21 +02:00
|
|
|
#endif
|
|
|
|
|
2011-05-12 17:26:27 +05:30
|
|
|
mutex_init(&dsi->lock);
|
|
|
|
sema_init(&dsi->bus_lock, 1);
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2012-08-21 13:18:23 -07:00
|
|
|
INIT_DEFERRABLE_WORK(&dsi->framedone_timeout_work,
|
|
|
|
dsi_framedone_timeout_work_callback);
|
2010-01-12 14:16:41 +02:00
|
|
|
|
drm/omap: dsi: allow DSI commands to be sent early
Panel drivers can send DSI commands in panel's prepare(), which happens
before the bridge's enable() is called. The OMAP DSI driver currently
only sets up the DSI interface at bridge's enable(), so prepare() cannot
be used to send DSI commands.
This patch fixes the issue by making it possible to enable the DSI
interface any time a command is about to be sent. Disabling the
interface is be done via delayed work.
Clarifications for the delayed disable work and the panel doing DSI
transactions:
bridge_enable: If the disable callback is called just before
bridge_enable takes the dsi_bus_lock, no problem, bridge_enable just
enables the interface again. If the callback is ran just after
bridge_enable's dsi_bus_unlock, no problem, dsi->video_enabled == true
so the callback does nothing.
bridge_disable: similar to bridge-enable, the callback won't do anything
if video_enabled == true, and after bridge-disable has turned the video
and the interface off, there's nothing to do for the callback.
omap_dsi_host_detach: this is called when the panel does
mipi_dsi_detach(), and we expect the panel to _not_ do any DSI
transactions after (or during) mipi_dsi_detatch(), so there are no
race conditions.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-85-tomi.valkeinen@ti.com
2020-12-15 12:46:57 +02:00
|
|
|
INIT_DEFERRABLE_WORK(&dsi->dsi_disable_work, omap_dsi_disable_work_callback);
|
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-16 14:43:17 -07:00
|
|
|
timer_setup(&dsi->te_timer, dsi_te_timeout, 0);
|
2009-10-28 11:59:56 +02:00
|
|
|
#endif
|
2013-12-17 13:53:28 +02:00
|
|
|
|
2018-02-13 14:00:35 +02:00
|
|
|
dsi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "proto");
|
|
|
|
dsi->proto_base = devm_ioremap_resource(dev, dsi_mem);
|
2017-05-07 00:29:09 +03:00
|
|
|
if (IS_ERR(dsi->proto_base))
|
|
|
|
return PTR_ERR(dsi->proto_base);
|
2013-12-17 13:53:28 +02:00
|
|
|
|
2018-02-13 14:00:35 +02:00
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
|
|
|
|
dsi->phy_base = devm_ioremap_resource(dev, res);
|
2017-05-07 00:29:09 +03:00
|
|
|
if (IS_ERR(dsi->phy_base))
|
|
|
|
return PTR_ERR(dsi->phy_base);
|
2013-12-17 13:53:28 +02:00
|
|
|
|
2018-02-13 14:00:35 +02:00
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll");
|
|
|
|
dsi->pll_base = devm_ioremap_resource(dev, res);
|
2017-05-07 00:29:09 +03:00
|
|
|
if (IS_ERR(dsi->pll_base))
|
|
|
|
return PTR_ERR(dsi->pll_base);
|
2012-01-25 13:31:04 +02:00
|
|
|
|
2018-02-13 14:00:35 +02:00
|
|
|
dsi->irq = platform_get_irq(pdev, 0);
|
2011-05-12 17:26:27 +05:30
|
|
|
if (dsi->irq < 0) {
|
2011-02-23 08:41:03 +00:00
|
|
|
DSSERR("platform_get_irq failed\n");
|
2012-01-25 13:31:04 +02:00
|
|
|
return -ENODEV;
|
2011-02-23 08:41:03 +00:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:00:35 +02:00
|
|
|
r = devm_request_irq(dev, dsi->irq, omap_dsi_irq_handler,
|
|
|
|
IRQF_SHARED, dev_name(dev), dsi);
|
2011-02-23 08:41:03 +00:00
|
|
|
if (r < 0) {
|
|
|
|
DSSERR("request_irq failed\n");
|
2012-01-25 13:31:04 +02:00
|
|
|
return r;
|
2011-02-23 08:41:03 +00:00
|
|
|
}
|
2009-10-28 11:59:56 +02:00
|
|
|
|
2018-03-05 00:10:55 +02:00
|
|
|
dsi->vdds_dsi_reg = devm_regulator_get(dev, "vdd");
|
|
|
|
if (IS_ERR(dsi->vdds_dsi_reg)) {
|
|
|
|
if (PTR_ERR(dsi->vdds_dsi_reg) != -EPROBE_DEFER)
|
|
|
|
DSSERR("can't get DSI VDD regulator\n");
|
|
|
|
return PTR_ERR(dsi->vdds_dsi_reg);
|
|
|
|
}
|
|
|
|
|
2017-08-05 01:44:10 +03:00
|
|
|
soc = soc_device_match(dsi_soc_devices);
|
|
|
|
if (soc)
|
|
|
|
dsi->data = soc->data;
|
|
|
|
else
|
|
|
|
dsi->data = of_match_node(dsi_of_match, dev->of_node)->data;
|
|
|
|
|
2017-08-05 01:43:57 +03:00
|
|
|
d = dsi->data->modules;
|
2017-05-07 00:42:26 +03:00
|
|
|
while (d->address != 0 && d->address != dsi_mem->start)
|
|
|
|
d++;
|
2012-08-21 15:35:42 +03:00
|
|
|
|
2017-05-07 00:42:26 +03:00
|
|
|
if (d->address == 0) {
|
|
|
|
DSSERR("unsupported DSI module\n");
|
|
|
|
return -ENODEV;
|
2012-08-21 15:35:42 +03:00
|
|
|
}
|
|
|
|
|
2017-05-07 00:42:26 +03:00
|
|
|
dsi->module_id = d->id;
|
|
|
|
|
2017-08-10 09:33:07 +03:00
|
|
|
if (dsi->data->model == DSI_MODEL_OMAP4 ||
|
|
|
|
dsi->data->model == DSI_MODEL_OMAP5) {
|
2017-08-05 01:43:53 +03:00
|
|
|
struct device_node *np;
|
|
|
|
|
|
|
|
/*
|
2017-08-10 09:33:07 +03:00
|
|
|
* The OMAP4/5 display DT bindings don't reference the padconf
|
2017-08-05 01:43:53 +03:00
|
|
|
* syscon. Our only option to retrieve it is to find it by name.
|
|
|
|
*/
|
2017-08-10 09:33:07 +03:00
|
|
|
np = of_find_node_by_name(NULL,
|
|
|
|
dsi->data->model == DSI_MODEL_OMAP4 ?
|
|
|
|
"omap4_padconf_global" : "omap5_padconf_global");
|
2017-08-05 01:43:53 +03:00
|
|
|
if (!np)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
dsi->syscon = syscon_node_to_regmap(np);
|
|
|
|
of_node_put(np);
|
|
|
|
}
|
|
|
|
|
2011-03-02 12:35:53 +05:30
|
|
|
/* DSI VCs initialization */
|
2020-12-15 12:46:35 +02:00
|
|
|
for (i = 0; i < ARRAY_SIZE(dsi->vc); i++)
|
2011-08-22 11:58:08 +05:30
|
|
|
dsi->vc[i].source = DSI_VC_SOURCE_L4;
|
2011-03-02 12:35:53 +05:30
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
r = dsi_get_clocks(dsi);
|
2012-01-25 13:31:04 +02:00
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
2018-02-13 14:00:35 +02:00
|
|
|
pm_runtime_enable(dev);
|
2012-01-25 13:31:04 +02:00
|
|
|
|
2011-10-12 15:05:59 +03:00
|
|
|
/* DSI on OMAP3 doesn't have register DSI_GNQ, set number
|
|
|
|
* of data to 3 by default */
|
2018-11-10 13:16:53 +02:00
|
|
|
if (dsi->data->quirks & DSI_QUIRK_GNQ) {
|
|
|
|
dsi_runtime_get(dsi);
|
2011-10-12 15:05:59 +03:00
|
|
|
/* NB_DATA_LANES */
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi->num_lanes_supported = 1 + REG_GET(dsi, DSI_GNQ, 11, 9);
|
2018-11-10 13:16:53 +02:00
|
|
|
dsi_runtime_put(dsi);
|
|
|
|
} else {
|
2011-10-12 15:05:59 +03:00
|
|
|
dsi->num_lanes_supported = 3;
|
2018-11-10 13:16:53 +02:00
|
|
|
}
|
2011-05-16 15:17:08 +05:30
|
|
|
|
2020-12-15 12:45:45 +02:00
|
|
|
dsi->host.ops = &omap_dsi_host_ops;
|
|
|
|
dsi->host.dev = &pdev->dev;
|
|
|
|
|
2020-12-15 12:46:21 +02:00
|
|
|
r = dsi_probe_of(dsi);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("Invalid DSI DT data\n");
|
|
|
|
goto err_pm_disable;
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:45:45 +02:00
|
|
|
r = mipi_dsi_host_register(&dsi->host);
|
|
|
|
if (r < 0) {
|
|
|
|
dev_err(&pdev->dev, "failed to register DSI host: %d\n", r);
|
2018-11-21 17:09:12 +01:00
|
|
|
goto err_pm_disable;
|
|
|
|
}
|
|
|
|
|
2018-03-02 22:13:06 +02:00
|
|
|
r = dsi_init_output(dsi);
|
|
|
|
if (r)
|
2020-12-15 12:45:45 +02:00
|
|
|
goto err_dsi_host_unregister;
|
2012-09-26 16:30:49 +05:30
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
r = component_add(&pdev->dev, &dsi_component_ops);
|
|
|
|
if (r)
|
2018-11-21 17:09:12 +01:00
|
|
|
goto err_uninit_output;
|
2012-08-21 15:35:42 +03:00
|
|
|
|
2009-10-28 11:59:56 +02:00
|
|
|
return 0;
|
2011-05-27 10:52:19 +03:00
|
|
|
|
2018-03-02 21:38:21 +02:00
|
|
|
err_uninit_output:
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_uninit_output(dsi);
|
2020-12-15 12:45:45 +02:00
|
|
|
err_dsi_host_unregister:
|
|
|
|
mipi_dsi_host_unregister(&dsi->host);
|
2018-03-02 22:13:06 +02:00
|
|
|
err_pm_disable:
|
2018-02-13 14:00:35 +02:00
|
|
|
pm_runtime_disable(dev);
|
2009-10-28 11:59:56 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
static int dsi_remove(struct platform_device *pdev)
|
2009-10-28 11:59:56 +02:00
|
|
|
{
|
2018-03-03 18:52:59 +02:00
|
|
|
struct dsi_data *dsi = platform_get_drvdata(pdev);
|
2011-05-12 17:26:27 +05:30
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
component_del(&pdev->dev, &dsi_component_ops);
|
2014-10-22 14:49:14 +03:00
|
|
|
|
2018-02-13 14:00:33 +02:00
|
|
|
dsi_uninit_output(dsi);
|
2012-09-26 16:30:49 +05:30
|
|
|
|
2020-12-15 12:45:45 +02:00
|
|
|
mipi_dsi_host_unregister(&dsi->host);
|
2019-01-11 05:50:34 +02:00
|
|
|
|
2018-03-03 18:52:59 +02:00
|
|
|
pm_runtime_disable(&pdev->dev);
|
2011-05-27 10:52:19 +03:00
|
|
|
|
2013-05-03 13:42:24 +03:00
|
|
|
if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) {
|
|
|
|
regulator_disable(dsi->vdds_dsi_reg);
|
|
|
|
dsi->vdds_dsi_enabled = false;
|
2011-01-24 06:22:02 +00:00
|
|
|
}
|
OMAPDSS: componentize omapdss
omapdss kernel module contains drivers for multiple devices, one for
each DSS submodule. The probing we have at the moment is a mess, and
doesn't give us proper deferred probing nor ensure that all the devices
are probed before omapfb/omapdrm start using omapdss.
This patch solves the mess by using the component system for DSS
submodules.
The changes to all DSS submodules (dispc, dpi, dsi, hdmi4/5, rfbi, sdi,
venc) are the same: probe & remove functions are changed to bind &
unbind, and new probe & remove functions are added which call
component_add/del.
The dss_core driver (dss.c) acts as a component master. Adding and
matching the components is simple: all dss device's child devices are
added as components.
However, we do have some dependencies between the drivers. The order in
which they should be probed is reflected by the list in core.c
(dss_output_drv_reg_funcs). The drivers are registered in that order,
which causes the components to be added in that order, which makes the
components to be bound in that order. This feels a bit fragile, and we
probably should improve the code to manage binds in random order.
However, for now, this works fine.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2015-06-04 15:22:23 +03:00
|
|
|
|
2011-01-24 06:22:02 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-27 10:52:19 +03:00
|
|
|
static int dsi_runtime_suspend(struct device *dev)
|
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
struct dsi_data *dsi = dev_get_drvdata(dev);
|
OMAPDSS: fix shared irq handlers
DSS uses shared irq handlers for DISPC and DSI, because on OMAP3, the
DISPC and DSI share the same irq line.
However, the irq handlers presume that the hardware is enabled, which,
in theory, may not be the case with shared irq handlers. So if an
interrupt happens while the DISPC/DSI is off, the kernel will halt as
the irq handler tries to access the DISPC/DSI registers.
In practice that should never happen, as both DSI and DISPC are in the
same power domain. So if there's an IRQ for one of them, the other is
also enabled. However, if CONFIG_DEBUG_SHIRQ is enabled, the kernel will
generate a spurious IRQ, which then causes the problem.
This patch adds an is_enabled field for both DISPC and DSI, which is
used to track if the HW is enabled. For DISPC the code is slightly more
complex, as the users of DISPC can register the interrupt handler, and
we want to hide the is_enabled handling from the users of DISPC.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2014-04-11 13:49:55 +03:00
|
|
|
|
|
|
|
dsi->is_enabled = false;
|
|
|
|
/* ensure the irq handler sees the is_enabled value */
|
|
|
|
smp_wmb();
|
|
|
|
/* wait for current handler to finish before turning the DSI off */
|
|
|
|
synchronize_irq(dsi->irq);
|
|
|
|
|
2011-05-27 10:52:19 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsi_runtime_resume(struct device *dev)
|
|
|
|
{
|
2018-02-13 14:00:34 +02:00
|
|
|
struct dsi_data *dsi = dev_get_drvdata(dev);
|
2011-05-27 10:52:19 +03:00
|
|
|
|
OMAPDSS: fix shared irq handlers
DSS uses shared irq handlers for DISPC and DSI, because on OMAP3, the
DISPC and DSI share the same irq line.
However, the irq handlers presume that the hardware is enabled, which,
in theory, may not be the case with shared irq handlers. So if an
interrupt happens while the DISPC/DSI is off, the kernel will halt as
the irq handler tries to access the DISPC/DSI registers.
In practice that should never happen, as both DSI and DISPC are in the
same power domain. So if there's an IRQ for one of them, the other is
also enabled. However, if CONFIG_DEBUG_SHIRQ is enabled, the kernel will
generate a spurious IRQ, which then causes the problem.
This patch adds an is_enabled field for both DISPC and DSI, which is
used to track if the HW is enabled. For DISPC the code is slightly more
complex, as the users of DISPC can register the interrupt handler, and
we want to hide the is_enabled handling from the users of DISPC.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2014-04-11 13:49:55 +03:00
|
|
|
dsi->is_enabled = true;
|
|
|
|
/* ensure the irq handler sees the is_enabled value */
|
|
|
|
smp_wmb();
|
|
|
|
|
2011-05-27 10:52:19 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dev_pm_ops dsi_pm_ops = {
|
|
|
|
.runtime_suspend = dsi_runtime_suspend,
|
|
|
|
.runtime_resume = dsi_runtime_resume,
|
2020-06-18 12:51:52 +03:00
|
|
|
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
2011-05-27 10:52:19 +03:00
|
|
|
};
|
|
|
|
|
2017-12-05 14:29:32 -06:00
|
|
|
struct platform_driver omap_dsihw_driver = {
|
OMAPDSS: componentize omapdss
omapdss kernel module contains drivers for multiple devices, one for
each DSS submodule. The probing we have at the moment is a mess, and
doesn't give us proper deferred probing nor ensure that all the devices
are probed before omapfb/omapdrm start using omapdss.
This patch solves the mess by using the component system for DSS
submodules.
The changes to all DSS submodules (dispc, dpi, dsi, hdmi4/5, rfbi, sdi,
venc) are the same: probe & remove functions are changed to bind &
unbind, and new probe & remove functions are added which call
component_add/del.
The dss_core driver (dss.c) acts as a component master. Adding and
matching the components is simple: all dss device's child devices are
added as components.
However, we do have some dependencies between the drivers. The order in
which they should be probed is reflected by the list in core.c
(dss_output_drv_reg_funcs). The drivers are registered in that order,
which causes the components to be added in that order, which makes the
components to be bound in that order. This feels a bit fragile, and we
probably should improve the code to manage binds in random order.
However, for now, this works fine.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2015-06-04 15:22:23 +03:00
|
|
|
.probe = dsi_probe,
|
|
|
|
.remove = dsi_remove,
|
2011-01-24 06:22:02 +00:00
|
|
|
.driver = {
|
2011-08-03 14:00:57 +03:00
|
|
|
.name = "omapdss_dsi",
|
2011-05-27 10:52:19 +03:00
|
|
|
.pm = &dsi_pm_ops,
|
2012-08-21 15:35:42 +03:00
|
|
|
.of_match_table = dsi_of_match,
|
2014-10-16 09:54:25 +03:00
|
|
|
.suppress_bind_attrs = true,
|
2011-01-24 06:22:02 +00:00
|
|
|
},
|
|
|
|
};
|