/** * DOC: VC4 DSI0/DSI1 module * * BCM2835 contains two DSI modules, DSI0 and DSI1. DSI0 is a * single-lane DSI controller, while DSI1 is a more modern 4-lane DSI * controller. * * Most Raspberry Pi boards expose DSI1 as their "DISPLAY" connector, * while the compute module brings both DSI0 and DSI1 out. * * This driver has been tested for DSI1 video-mode display only * currently, with most of the information necessary for DSI0 * hopefully present.
*/
# define DSI_TXPKT1C_DISPLAY_NO_MASK VC4_MASK(9, 8) # define DSI_TXPKT1C_DISPLAY_NO_SHIFT 8 /* Short, trigger, BTA, or a long packet that fits all in CMDFIFO. */ # define DSI_TXPKT1C_DISPLAY_NO_SHORT 0 /* Primary display where cmdfifo provides part of the payload and * pixelvalve the rest.
*/ # define DSI_TXPKT1C_DISPLAY_NO_PRIMARY 1 /* Secondary display where cmdfifo provides part of the payload and * pixfifo the rest.
*/ # define DSI_TXPKT1C_DISPLAY_NO_SECONDARY 2
# define DSI_TXPKT1C_CMD_CTRL_MASK VC4_MASK(5, 4) # define DSI_TXPKT1C_CMD_CTRL_SHIFT 4 /* Command only. Uses TXPKT1H and DISPLAY_NO */ # define DSI_TXPKT1C_CMD_CTRL_TX 0 /* Command with BTA for either ack or read data. */ # define DSI_TXPKT1C_CMD_CTRL_RX 1 /* Trigger according to TRIG_CMD */ # define DSI_TXPKT1C_CMD_CTRL_TRIG 2 /* BTA alone for getting error status after a command, or a TE trigger * without a previous command.
*/ # define DSI_TXPKT1C_CMD_CTRL_BTA 3
/* Signaled when the clock lane enters the given state. */ # define DSI1_INT_PHY_CLOCK_ULPS BIT(16) # define DSI1_INT_PHY_CLOCK_HS BIT(15) # define DSI1_INT_PHY_CLOCK_STOP BIT(14)
/* Contention on a line when trying to drive the line low */ # define DSI1_INT_ERR_CONT_LP1 BIT(9) # define DSI1_INT_ERR_CONT_LP0 BIT(8)
/* Control error: incorrect line state sequence on data lane 0. */ # define DSI1_INT_ERR_CONTROL BIT(7) /* LPDT synchronization error (bits received not a multiple of 8. */
# define DSI1_INT_ERR_SYNC_ESC BIT(6) /* Signaled after receiving an error packet from the display in * response to a read.
*/ # define DSI1_INT_RXPKT2 BIT(5) /* Signaled after receiving a packet. The header and optional short * response will be in RXPKT1H, and a long response will be in the * RXPKT_FIFO.
*/ # define DSI1_INT_RXPKT1 BIT(4) # define DSI1_INT_TXPKT2_DONE BIT(3) # define DSI1_INT_TXPKT2_END BIT(2) /* Signaled after all repeats of TXPKT1 are transferred. */ # define DSI1_INT_TXPKT1_DONE BIT(1) /* Signaled after each TXPKT1 repeat is scheduled. */ # define DSI1_INT_TXPKT1_END BIT(0)
#define DSI1_TST_SEL 0x78 #define DSI1_TST_MON 0x7c #define DSI1_PHY_TST1 0x80 #define DSI1_PHY_TST2 0x84 #define DSI1_PHY_FIFO_STAT 0x88 /* Actually, all registers in the range that aren't otherwise claimed * will return the ID.
*/ #define DSI1_ID 0x8c
struct vc4_dsi_variant { /* Whether we're on bcm2835's DSI0 or DSI1. */ unsignedint port;
kunit_fail_current_test("Accessing a register in a unit test!\n");
/* DSI0 should be able to write normally. */ if (!chan) {
writel(val, dsi->regs + offset); return;
}
*dsi->reg_dma_mem = val;
tx = chan->device->device_prep_dma_memcpy(chan,
dsi->reg_paddr + offset,
dsi->reg_dma_paddr,
4, 0); if (!tx) {
drm_err(drm, "Failed to set up DMA register write\n"); return;
}
cookie = tx->tx_submit(tx);
ret = dma_submit_error(cookie); if (ret) {
drm_err(drm, "Failed to submit DMA: %d\n", ret); return;
}
ret = dma_sync_wait(chan, cookie); if (ret)
drm_err(drm, "Failed to wait for DMA: %d\n", ret);
}
#define DSI_READ(offset) \
({ \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
readl(dsi->regs + (offset)); \
})
DSI_PORT_WRITE(STAT, stat_ulps);
DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) | phyc_ulps);
ret = wait_for((DSI_PORT_READ(STAT) & stat_ulps) == stat_ulps, 200); if (ret) {
dev_warn(&dsi->pdev->dev, "Timeout waiting for DSI ULPS entry: STAT 0x%08x",
DSI_PORT_READ(STAT));
DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps);
vc4_dsi_latch_ulps(dsi, false); return;
}
/* The DSI module can't be disabled while the module is * generating ULPS state. So, to be able to disable the * module, we have the AFE latch the ULPS state and continue * on to having the module enter STOP.
*/
vc4_dsi_latch_ulps(dsi, ulps);
DSI_PORT_WRITE(STAT, stat_stop);
DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps);
ret = wait_for((DSI_PORT_READ(STAT) & stat_stop) == stat_stop, 200); if (ret) {
dev_warn(&dsi->pdev->dev, "Timeout waiting for DSI STOP entry: STAT 0x%08x",
DSI_PORT_READ(STAT));
DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); return;
}
}
static u32
dsi_hs_timing(u32 ui_ns, u32 ns, u32 ui)
{ /* The HS timings have to be rounded up to a multiple of 8 * because we're using the byte clock.
*/ return roundup(ui + DIV_ROUND_UP(ns, ui_ns), 8);
}
/* ESC always runs at 100Mhz. */ #define ESC_TIME_NS 10
/* Extends the mode's blank intervals to handle BCM2835's integer-only * DSI PLL divider. * * On 2835, PLLD is set to 2Ghz, and may not be changed by the display * driver since most peripherals are hanging off of the PLLD_PER * divider. PLLD_DSI1, which drives our DSI bit clock (and therefore * the pixel clock), only has an integer divider off of DSI. * * To get our panel mode to refresh at the expected 60Hz, we need to * extend the horizontal blank time. This means we drive a * higher-than-expected clock rate to the panel, but that's what the * firmware does too.
*/ staticbool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge, conststruct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
{ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock); unsignedlong parent_rate = clk_get_rate(phy_parent); unsignedlong pixel_clock_hz = mode->clock * 1000; unsignedlong pll_clock = pixel_clock_hz * dsi->divider; int divider;
/* Find what divider gets us a faster clock than the requested * pixel clock.
*/ for (divider = 1; divider < 255; divider++) { if (parent_rate / (divider + 1) < pll_clock) break;
}
/* Now that we've picked a PLL divider, calculate back to its * pixel clock.
*/
pll_clock = parent_rate / divider;
pixel_clock_hz = pll_clock / dsi->divider;
adjusted_mode->clock = pixel_clock_hz / 1000;
/* Given the new pixel clock, adjust HFP to keep vrefresh the same. */
adjusted_mode->htotal = adjusted_mode->clock * mode->htotal /
mode->clock;
adjusted_mode->hsync_end += adjusted_mode->htotal - mode->htotal;
adjusted_mode->hsync_start += adjusted_mode->htotal - mode->htotal;
ret = pm_runtime_resume_and_get(dev); if (ret) {
drm_err(bridge->dev, "Failed to runtime PM enable on DSI%d\n", dsi->variant->port); return;
}
if (debug_dump_regs) { struct drm_printer p = drm_info_printer(&dsi->pdev->dev);
dev_info(&dsi->pdev->dev, "DSI regs before:\n");
drm_print_regset32(&p, &dsi->regset);
}
/* * Retrieve the CRTC adjusted mode. This requires a little dance to go * from the bridge to the encoder, to the connector and to the CRTC.
*/
connector = drm_atomic_get_new_connector_for_encoder(state,
bridge->encoder);
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
mode = &crtc_state->adjusted_mode;
pixel_clock_hz = mode->clock * 1000;
/* Round up the clk_set_rate() request slightly, since * PLLD_DSI1 is an integer divider and its rate selection will * never round up.
*/
phy_clock = (pixel_clock_hz + 1000) * dsi->divider;
ret = clk_set_rate(dsi->pll_phy_clock, phy_clock); if (ret) {
dev_err(&dsi->pdev->dev, "Failed to set phy clock to %ld: %d\n", phy_clock, ret);
}
/* Reset the DSI and all its fifos. */
DSI_PORT_WRITE(CTRL,
DSI_CTRL_SOFT_RESET_CFG |
DSI_PORT_BIT(CTRL_RESET_FIFOS));
if (dsi->lanes < 4)
afec0 |= DSI1_PHY_AFEC0_PD_DLANE3; if (dsi->lanes < 3)
afec0 |= DSI1_PHY_AFEC0_PD_DLANE2; if (dsi->lanes < 2)
afec0 |= DSI1_PHY_AFEC0_PD_DLANE1;
afec0 |= DSI1_PHY_AFEC0_RESET;
DSI_PORT_WRITE(PHY_AFEC0, afec0);
DSI_PORT_WRITE(PHY_AFEC1, 0);
/* AFEC reset hold time */
mdelay(1);
}
ret = clk_prepare_enable(dsi->escape_clock); if (ret) {
drm_err(bridge->dev, "Failed to turn on DSI escape clock: %d\n",
ret); return;
}
ret = clk_prepare_enable(dsi->pll_phy_clock); if (ret) {
drm_err(bridge->dev, "Failed to turn on DSI PLL: %d\n", ret); return;
}
hs_clock = clk_get_rate(dsi->pll_phy_clock);
/* Yes, we set the DSI0P/DSI1P pixel clock to the byte rate, * not the pixel clock rate. DSIxP take from the APHY's byte, * DDR2, or DDR4 clock (we use byte) and feed into the PV at * that rate. Separately, a value derived from PIX_CLK_DIV * and HS_CLKC is fed into the PV to divide down to the actual * pixel clock for pushing pixels into DSI.
*/
dsip_clock = phy_clock / 8;
ret = clk_set_rate(dsi->pixel_clock, dsip_clock); if (ret) {
dev_err(dev, "Failed to set pixel clock to %ldHz: %d\n",
dsip_clock, ret);
}
ret = clk_prepare_enable(dsi->pixel_clock); if (ret) {
drm_err(bridge->dev, "Failed to turn on DSI pixel clock: %d\n", ret); return;
}
/* How many ns one DSI unit interval is. Note that the clock * is DDR, so there's an extra divide by 2.
*/
ui_ns = DIV_ROUND_UP(500000000, hs_clock);
/* T_INIT is how long STOP is driven after power-up to * indicate to the slave (also coming out of power-up) that * master init is complete, and should be greater than the * maximum of two value: T_INIT,MASTER and T_INIT,SLAVE. The * D-PHY spec gives a minimum 100us for T_INIT,MASTER and * T_INIT,SLAVE, while allowing protocols on top of it to give * greater minimums. The vc4 firmware uses an extremely * conservative 5ms, and we maintain that here.
*/
DSI_PORT_WRITE(HS_DLT5, VC4_SET_FIELD(dsi_hs_timing(ui_ns,
5 * 1000 * 1000, 0),
DSI_HS_DLT5_INIT));
/* Set up DISP1 for transferring long command payloads through * the pixfifo.
*/
DSI_PORT_WRITE(DISP1_CTRL,
VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE,
DSI_DISP1_PFORMAT) |
DSI_DISP1_ENABLE);
/* Ungate the block. */ if (dsi->variant->port == 0)
DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI0_CTRL_CTRL0); else
DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN);
/* Bring AFE out of reset. */
DSI_PORT_WRITE(PHY_AFEC0,
DSI_PORT_READ(PHY_AFEC0) &
~DSI_PORT_BIT(PHY_AFEC0_RESET));
pkth |= VC4_SET_FIELD(packet.header[0], DSI_TXPKT1H_BC_DT);
pkth |= VC4_SET_FIELD(packet.header[1] |
(packet.header[2] << 8),
DSI_TXPKT1H_BC_PARAM); if (is_long) { /* Divide data across the various FIFOs we have available. * The command FIFO takes byte-oriented data, but is of * limited size. The pixel FIFO (never actually used for * pixel data in reality) is word oriented, and substantially * larger. So, we use the pixel FIFO for most of the data, * sending the residual bytes in the command FIFO at the start. * * With this arrangement, the command FIFO will never get full.
*/ if (packet.payload_length <= 16) {
cmd_fifo_len = packet.payload_length;
pix_fifo_len = 0;
} else {
cmd_fifo_len = (packet.payload_length %
DSI_PIX_FIFO_WIDTH);
pix_fifo_len = ((packet.payload_length - cmd_fifo_len) /
DSI_PIX_FIFO_WIDTH);
}
for (i = 0; i < cmd_fifo_len; i++)
DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]); for (i = 0; i < pix_fifo_len; i++) { const u8 *pix = packet.payload + cmd_fifo_len + i * 4;
/* * Initial handler for port 1 where we need the reg_dma workaround. * The register DMA writes sleep, so we can't do it in the top half. * Instead we use IRQF_ONESHOT so that the IRQ gets disabled in the * parent interrupt contrller until our interrupt thread is done.
*/ static irqreturn_t vc4_dsi_irq_defer_to_thread_handler(int irq, void *data)
{ struct vc4_dsi *dsi = data;
u32 stat = DSI_PORT_READ(INT_STAT);
if (!stat) return IRQ_NONE;
return IRQ_WAKE_THREAD;
}
/* * Normal IRQ handler for port 0, or the threaded IRQ handler for port * 1 where we need the reg_dma workaround.
*/ static irqreturn_t vc4_dsi_irq_handler(int irq, void *data)
{ struct vc4_dsi *dsi = data;
u32 stat = DSI_PORT_READ(INT_STAT);
irqreturn_t ret = IRQ_NONE;
/* We just use core fixed factor clock ops for the PHY * clocks. The clocks are actually gated by the * PHY_AFEC0_DDRCLK_EN bits, which we should be * setting if we use the DDR/DDR2 clocks. However, * vc4_dsi_encoder_enable() is setting up both AFEC0, * setting both our parent DSI PLL's rate and this * clock's rate, so it knows if DDR/DDR2 are going to * be used and could enable the gates itself.
*/
fix->mult = 1;
fix->div = phy_clocks[i].div;
fix->hw.init = &init;
if (DSI_PORT_READ(ID) != DSI_ID_VALUE) {
dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n",
DSI_PORT_READ(ID), DSI_ID_VALUE); return -ENODEV;
}
/* DSI1 on BCM2835/6/7 has a broken AXI slave that doesn't respond to * writes from the ARM. It does handle writes from the DMA engine, * so set up a channel for talking to it.
*/ if (dsi->variant->broken_axi_workaround) {
dma_cap_mask_t dma_mask;
dsi->reg_dma_mem = dma_alloc_coherent(dev, 4,
&dsi->reg_dma_paddr,
GFP_KERNEL); if (!dsi->reg_dma_mem) {
drm_err(drm, "Failed to get DMA memory\n"); return -ENOMEM;
}
ret = devm_add_action_or_reset(dev, vc4_dsi_dma_mem_release, dsi); if (ret) return ret;
dsi->reg_dma_chan = dma_request_chan_by_mask(&dma_mask); if (IS_ERR(dsi->reg_dma_chan)) {
ret = PTR_ERR(dsi->reg_dma_chan); if (ret != -EPROBE_DEFER)
drm_err(drm, "Failed to get DMA channel: %d\n",
ret); return ret;
}
ret = devm_add_action_or_reset(dev, vc4_dsi_dma_chan_release, dsi); if (ret) return ret;
/* Get the physical address of the device's registers. The * struct resource for the regs gives us the bus address * instead.
*/
dsi->reg_paddr = be32_to_cpup(of_get_address(dev->of_node,
0, NULL, NULL));
}
init_completion(&dsi->xfer_completion); /* At startup enable error-reporting interrupts and nothing else. */
DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); /* Clear any existing interrupt state. */
DSI_PORT_WRITE(INT_STAT, DSI_PORT_READ(INT_STAT));
if (dsi->reg_dma_mem)
ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
vc4_dsi_irq_defer_to_thread_handler,
vc4_dsi_irq_handler,
IRQF_ONESHOT, "vc4 dsi", dsi); else
ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
vc4_dsi_irq_handler, 0, "vc4 dsi", dsi); if (ret) { if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get interrupt: %d\n", ret); return ret;
}
dsi->escape_clock = devm_clk_get(dev, "escape"); if (IS_ERR(dsi->escape_clock)) {
ret = PTR_ERR(dsi->escape_clock); if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get escape clock: %d\n", ret); return ret;
}
dsi->pll_phy_clock = devm_clk_get(dev, "phy"); if (IS_ERR(dsi->pll_phy_clock)) {
ret = PTR_ERR(dsi->pll_phy_clock); if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get phy clock: %d\n", ret); return ret;
}
dsi->pixel_clock = devm_clk_get(dev, "pixel"); if (IS_ERR(dsi->pixel_clock)) {
ret = PTR_ERR(dsi->pixel_clock); if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get pixel clock: %d\n", ret); return ret;
}
dsi->out_bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0); if (IS_ERR(dsi->out_bridge)) return PTR_ERR(dsi->out_bridge);
/* The esc clock rate is supposed to always be 100Mhz. */
ret = clk_set_rate(dsi->escape_clock, 100 * 1000000); if (ret) {
dev_err(dev, "Failed to set esc clock: %d\n", ret); return ret;
}
ret = vc4_dsi_init_phy_clocks(dsi); if (ret) return ret;
ret = drmm_encoder_init(drm, encoder,
&vc4_dsi_encoder_funcs,
DRM_MODE_ENCODER_DSI,
NULL); if (ret) return ret;
ret = devm_pm_runtime_enable(dev); if (ret) return ret;
ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, 0); if (ret) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.