summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2013-11-08 12:32:57 +0100
committerThierry Reding <treding@nvidia.com>2014-01-10 10:15:23 +0100
commita0898b2c9a6d31371d7383aad2ea0c6e85964efb (patch)
tree38731b00f0f991133c6483d071986bca2fbce755
parent8733f544fd385869fe79b1f658fedf393ae9d798 (diff)
WIP: drm/tegra: Implement DSI transfers
Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/gpu/drm/tegra/dsi.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index d452faab0235..bdca68651612 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -783,6 +783,131 @@ static void tegra_dsi_initialize(struct tegra_dsi *dsi)
tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE);
}
+static int tegra_dsi_set_lp_clk(struct tegra_dsi *dsi, unsigned long pclk)
+{
+ unsigned long bclk, timeout, value;
+ struct clk *clk, *parent, *base;
+ unsigned int mul, div;
+ int err;
+
+ err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
+ if (err < 0)
+ return err;
+
+ bclk = pclk * 8;
+
+ clk = clk_get_parent(dsi->clk);
+ parent = clk_get_parent(clk);
+ base = clk_get_parent(parent);
+
+ err = clk_set_rate(base, pclk * 2);
+ if (err < 0)
+ return err;
+
+ /* one frame high-speed transmission timeout */
+ timeout = bclk / 512;
+ value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
+ tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0);
+
+ /* 2 ms peripheral timeout for panel */
+ timeout = 2 * bclk / 512 * 1000;
+ value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
+ tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1);
+
+ value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
+ tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
+
+ return 0;
+}
+
+static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host,
+ struct mipi_dsi_msg *msg)
+{
+ struct tegra_dsi *dsi = host_to_tegra(host);
+ unsigned long value, timeout;
+ const u8 *tx = msg->tx_buf;
+ unsigned int count = 0, i;
+ int err;
+
+ dev_info(dsi->dev, "> %s(host=%p, msg=%p)\n", __func__, host, msg);
+
+ /* XXX */
+ drm_panel_enable(dsi->output.panel);
+
+ value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
+ value |= DSI_POWER_CONTROL_ENABLE;
+ tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
+ usleep_range(300, 1000);
+
+ err = tegra_dsi_set_lp_clk(dsi, 10000000);
+ if (err < 0) {
+ dev_err(dsi->dev, "failed to setup low-power clock: %d\n", err);
+ return err;
+ }
+
+ tegra_dsi_writel(dsi, DSI_HOST_FIFO_DEPTH, DSI_MAX_THRESHOLD);
+
+ value = tegra_dsi_readl(dsi, DSI_CONTROL);
+ value = 0x00003031;
+ tegra_dsi_writel(dsi, value, DSI_CONTROL);
+
+ value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL);
+ value = 0x00102003;
+ tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
+
+ if (msg->rx_buf && msg->rx_len > 0) {
+ value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL);
+ value |= DSI_HOST_CONTROL_BTA;
+ tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
+ }
+
+ value = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
+
+ value |= tx[0] << 8;
+ value |= tx[1] << 16;
+
+ tegra_dsi_writel(dsi, value, DSI_WR_DATA);
+
+ tegra_dsi_writel(dsi, 0x00000002, DSI_TRIGGER);
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ while (true) {
+ value = tegra_dsi_readl(dsi, DSI_TRIGGER);
+ if ((value & 0x00000002) == 0)
+ break;
+
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ usleep_range(25, 100);
+ }
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ while (true) {
+ usleep_range(1000, 2000);
+
+ value = tegra_dsi_readl(dsi, DSI_STATUS);
+ count = value & 0x1f;
+
+ if (count > 0)
+ break;
+
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ }
+
+ for (i = 0; i < count; i++)
+ tegra_dsi_readl(dsi, DSI_RD_DATA);
+
+ /* XXX */
+ drm_panel_disable(dsi->output.panel);
+
+ dev_info(dsi->dev, "< %s()\n", __func__);
+ return 0;
+}
+
static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
@@ -796,6 +921,27 @@ static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
if (output->panel) {
if (output->connector.dev)
drm_helper_hpd_irq_event(output->connector.dev);
+
+ if (0) {
+ struct mipi_dsi_msg msg;
+ u8 tx[2], rx[2];
+ ssize_t err;
+
+ rx[0] = rx[1] = 0;
+ tx[0] = tx[1] = 0;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.channel = 0;
+ msg.type = 0x05;
+ msg.tx_buf = tx;
+ msg.tx_len = 2;
+ msg.rx_buf = rx;
+ msg.rx_len = 2;
+
+ err = host->ops->transfer(&dsi->host, &msg);
+ if (err < 0)
+ dev_err(dsi->dev, "dsi_host_transfer() failed: %zd\n", err);
+ }
}
return 0;
@@ -820,6 +966,7 @@ static int tegra_dsi_host_detach(struct mipi_dsi_host *host,
static const struct mipi_dsi_host_ops tegra_dsi_host_ops = {
.attach = tegra_dsi_host_attach,
.detach = tegra_dsi_host_detach,
+ .transfer = tegra_dsi_host_transfer,
};
static int tegra_dsi_probe(struct platform_device *pdev)