/* * This file is part of the coreboot project. * * Copyright 2014 Google Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "chip.h" #include #include #include #include #include #include "jdi_25x18_display/panel-jdi-lpm102a188a.h" #include struct tegra_mipi_device mipi_device_data[NUM_DSI]; struct tegra_dsi dsi_data[NUM_DSI] = { { .regs = (void *)TEGRA_DSIA_BASE, .channel = 0, .slave = &dsi_data[DSI_B], .master = NULL, .video_fifo_depth = MAX_DSI_VIDEO_FIFO_DEPTH, .host_fifo_depth = MAX_DSI_HOST_FIFO_DEPTH, }, { .regs = (void *)TEGRA_DSIB_BASE, .channel = 0, .slave = NULL, .master = &dsi_data[DSI_A], .video_fifo_depth = MAX_DSI_VIDEO_FIFO_DEPTH, .host_fifo_depth = MAX_DSI_HOST_FIFO_DEPTH, }, }; static const u32 init_reg[] = { DSI_INT_ENABLE, DSI_INT_STATUS, DSI_INT_MASK, DSI_INIT_SEQ_DATA_0, DSI_INIT_SEQ_DATA_1, DSI_INIT_SEQ_DATA_2, DSI_INIT_SEQ_DATA_3, DSI_INIT_SEQ_DATA_4, DSI_INIT_SEQ_DATA_5, DSI_INIT_SEQ_DATA_6, DSI_INIT_SEQ_DATA_7, DSI_INIT_SEQ_DATA_15, DSI_DCS_CMDS, DSI_PKT_SEQ_0_LO, DSI_PKT_SEQ_1_LO, DSI_PKT_SEQ_2_LO, DSI_PKT_SEQ_3_LO, DSI_PKT_SEQ_4_LO, DSI_PKT_SEQ_5_LO, DSI_PKT_SEQ_0_HI, DSI_PKT_SEQ_1_HI, DSI_PKT_SEQ_2_HI, DSI_PKT_SEQ_3_HI, DSI_PKT_SEQ_4_HI, DSI_PKT_SEQ_5_HI, DSI_CONTROL, DSI_HOST_CONTROL, DSI_PAD_CONTROL_0, DSI_PAD_CONTROL_CD, DSI_SOL_DELAY, DSI_MAX_THRESHOLD, DSI_TRIGGER, DSI_TX_CRC, DSI_INIT_SEQ_CONTROL, DSI_PKT_LEN_0_1, DSI_PKT_LEN_2_3, DSI_PKT_LEN_4_5, DSI_PKT_LEN_6_7, }; static inline struct tegra_dsi *host_to_tegra(struct mipi_dsi_host *host) { return container_of(host, struct tegra_dsi, host); } /* * non-burst mode with sync pulses */ static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = { [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | PKT_LP, [ 1] = 0, [ 2] = PKT_ID0(MIPI_DSI_V_SYNC_END) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | PKT_LP, [ 3] = 0, [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | PKT_LP, [ 5] = 0, [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | PKT_LP, [ 9] = 0, [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), }; /* * non-burst mode with sync events */ static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = { [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | PKT_LP, [ 1] = 0, [ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | PKT_LP, [ 3] = 0, [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | PKT_LP, [ 5] = 0, [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | PKT_LP, [ 9] = 0, [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), }; static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = { [ 0] = 0, [ 1] = 0, [ 2] = 0, [ 3] = 0, [ 4] = 0, [ 5] = 0, [ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP, [ 7] = 0, [ 8] = 0, [ 9] = 0, [10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP, [11] = 0, }; static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi) { int err; err = mipi_dphy_set_timing(dsi); if (err < 0) { printk(BIOS_ERR, "failed to set D-PHY timing: %d\n", err); return err; } if (dsi->slave) tegra_dsi_set_phy_timing(dsi->slave); return 0; } static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format, unsigned int *mulp, unsigned int *divp) { switch (format) { case MIPI_DSI_FMT_RGB666_PACKED: case MIPI_DSI_FMT_RGB888: *mulp = 3; *divp = 1; break; case MIPI_DSI_FMT_RGB565: *mulp = 2; *divp = 1; break; case MIPI_DSI_FMT_RGB666: *mulp = 9; *divp = 4; break; default: return -EINVAL; } return 0; } static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format, enum tegra_dsi_format *fmt) { switch (format) { case MIPI_DSI_FMT_RGB888: *fmt = TEGRA_DSI_FORMAT_24P; break; case MIPI_DSI_FMT_RGB666: *fmt = TEGRA_DSI_FORMAT_18NP; break; case MIPI_DSI_FMT_RGB666_PACKED: *fmt = TEGRA_DSI_FORMAT_18P; break; case MIPI_DSI_FMT_RGB565: *fmt = TEGRA_DSI_FORMAT_16P; break; default: return -EINVAL; } return 0; } static void tegra_dsi_ganged_enable(struct tegra_dsi *dsi, unsigned int start, unsigned int size) { u32 value; tegra_dsi_writel(dsi, start, DSI_GANGED_MODE_START); tegra_dsi_writel(dsi, size << 16 | size, DSI_GANGED_MODE_SIZE); value = DSI_GANGED_MODE_CONTROL_ENABLE; tegra_dsi_writel(dsi, value, DSI_GANGED_MODE_CONTROL); } static void tegra_dsi_enable(struct tegra_dsi *dsi) { u32 value; value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); value |= DSI_POWER_CONTROL_ENABLE; tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); if (dsi->slave) tegra_dsi_enable(dsi->slave); } static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, const struct soc_nvidia_tegra210_config *mode) { unsigned int hact, hsw, hbp, hfp, i, mul, div; enum tegra_dsi_format format; const u32 *pkt_seq; u32 value; int err; if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { printk(BIOS_SPEW, "Non-burst video mode with sync pulses\n"); pkt_seq = pkt_seq_video_non_burst_sync_pulses; } else if (dsi->flags & MIPI_DSI_MODE_VIDEO) { printk(BIOS_SPEW, "Non-burst video mode with sync events\n"); pkt_seq = pkt_seq_video_non_burst_sync_events; } else { printk(BIOS_SPEW, "Command mode\n"); pkt_seq = pkt_seq_command_mode; } err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); if (err < 0) return err; err = tegra_dsi_get_format(dsi->format, &format); if (err < 0) return err; value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) | DSI_CONTROL_LANES(dsi->lanes - 1) | DSI_CONTROL_SOURCE(pipe); tegra_dsi_writel(dsi, value, DSI_CONTROL); tegra_dsi_writel(dsi, dsi->video_fifo_depth, DSI_MAX_THRESHOLD); value = DSI_HOST_CONTROL_HS; tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); value = tegra_dsi_readl(dsi, DSI_CONTROL); if (dsi->flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) value |= DSI_CONTROL_HS_CLK_CTRL; value &= ~DSI_CONTROL_TX_TRIG(3); /* enable DCS commands for command mode */ if (dsi->flags & MIPI_DSI_MODE_VIDEO) value &= ~DSI_CONTROL_DCS_ENABLE; else value |= DSI_CONTROL_DCS_ENABLE; value |= DSI_CONTROL_VIDEO_ENABLE; value &= ~DSI_CONTROL_HOST_ENABLE; tegra_dsi_writel(dsi, value, DSI_CONTROL); for (i = 0; i < NUM_PKT_SEQ; i++) tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i); if (dsi->flags & MIPI_DSI_MODE_VIDEO) { /* horizontal active pixels */ hact = mode->xres * mul / div; /* horizontal sync width */ hsw = (hsync_end(mode) - hsync_start(mode)) * mul / div; /* horizontal back porch */ hbp = (htotal(mode) - hsync_end(mode)) * mul / div; if ((dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) hbp += hsw; /* horizontal front porch */ hfp = (hsync_start(mode) - mode->xres) * mul / div; /* subtract packet overhead */ hsw -= 10; hbp -= 14; hfp -= 8; tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1); tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3); tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5); tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7); /* set SOL delay (for non-burst mode only) */ tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY); /* TODO: implement ganged mode */ } else { u16 bytes; if (dsi->ganged_mode) { /* * For ganged mode, assume symmetric left-right mode. */ bytes = 1 + (mode->xres / 2) * mul / div; } else { /* 1 byte (DCS command) + pixel data */ bytes = 1 + mode->xres * mul / div; } tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1); tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_2_3); tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_4_5); tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_6_7); value = MIPI_DCS_WRITE_MEMORY_START << 8 | MIPI_DCS_WRITE_MEMORY_CONTINUE; tegra_dsi_writel(dsi, value, DSI_DCS_CMDS); /* set SOL delay */ if (dsi->ganged_mode) { unsigned long delay, bclk, bclk_ganged; unsigned int lanes = dsi->ganged_lanes; /* SOL to valid, valid to FIFO and FIFO write delay */ delay = 4 + 4 + 2; delay = DIV_ROUND_UP(delay * mul, div * lanes); /* FIFO read delay */ delay = delay + 6; bclk = DIV_ROUND_UP(htotal(mode) * mul, div * lanes); bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); value = bclk - bclk_ganged + delay + 20; } else { /* TODO: revisit for non-ganged mode */ value = 8 * mul / div; } tegra_dsi_writel(dsi, value, DSI_SOL_DELAY); } if (dsi->slave) { err = tegra_dsi_configure(dsi->slave, pipe, mode); if (err < 0) return err; /* * enable ganged mode */ if (dsi->ganged_mode) { tegra_dsi_ganged_enable(dsi, mode->xres / 2, mode->xres / 2); tegra_dsi_ganged_enable(dsi->slave, 0, mode->xres / 2); } } return 0; } static int tegra_output_dsi_enable(struct tegra_dsi *dsi, const struct soc_nvidia_tegra210_config *config) { int err; if (dsi->enabled) return 0; err = tegra_dsi_configure(dsi, 0, config); if (err < 0) { printk(BIOS_ERR, "DSI configuration failed\n"); return err; } /* enable DSI controller */ tegra_dsi_enable(dsi); dsi->enabled = true; return 0; } static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk, unsigned int vrefresh) { unsigned int timeout; u32 value; /* one frame high-speed transmission timeout */ timeout = (bclk / vrefresh) / 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); if (dsi->slave) tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh); } static int tegra_output_dsi_setup_clock(struct tegra_dsi *dsi, const struct soc_nvidia_tegra210_config *config) { unsigned int mul, div, num_lanes; unsigned long bclk; unsigned long pclk = config->pixel_clock; int plld; int err; struct display_controller *disp_ctrl = (void *)config->display_controller; unsigned int shift_clk_div; err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); if (err < 0) return err; /* * In ganged mode, account for the total number of lanes across both * DSI channels so that the bit clock is properly computed. */ if (dsi->ganged_mode) num_lanes = dsi->ganged_lanes; else num_lanes = dsi->lanes; /* compute byte clock */ bclk = (pclk * mul) / (div * num_lanes); /* * Compute bit clock and round up to the next MHz. */ plld = DIV_ROUND_UP(bclk * 8, USECS_PER_SEC) * USECS_PER_SEC; /* * the actual rate on PLLD_OUT0 is 1/2 plld */ dsi->clk_rate = plld / 2; if (dsi->slave) dsi->slave->clk_rate = dsi->clk_rate; /* set up plld */ plld = clock_configure_plld(plld); if (plld == 0) { printk(BIOS_ERR, "%s: clock init failed\n", __func__); return -1; } else printk(BIOS_INFO, "%s: plld is configured to: %u\n", __func__, plld); /* * Derive pixel clock from bit clock using the shift clock divider. * Note that this is only half of what we would expect, but we need * that to make up for the fact that we divided the bit clock by a * factor of two above. */ shift_clk_div = ((8 * mul) / (div * num_lanes)) - 2; update_display_shift_clock_divider(disp_ctrl, shift_clk_div); tegra_dsi_set_timeout(dsi, bclk, config->refresh); return plld/1000000; } static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) { unsigned long value; value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); return 0; } static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi) { u32 value; tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0); tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2); tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3); tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4); /* start calibration */ tegra_dsi_pad_enable(dsi); value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) | DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) | DSI_PAD_OUT_CLK(0x0); tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2); value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3); return tegra_mipi_calibrate(dsi->mipi); } static const char *const error_report[16] = { "SoT Error", "SoT Sync Error", "EoT Sync Error", "Escape Mode Entry Command Error", "Low-Power Transmit Sync Error", "Peripheral Timeout Error", "False Control Error", "Contention Detected", "ECC Error, single-bit", "ECC Error, multi-bit", "Checksum Error", "DSI Data Type Not Recognized", "DSI VC ID Invalid", "Invalid Transmission Length", "Reserved", "DSI Protocol Violation", }; static int tegra_dsi_read_response(struct tegra_dsi *dsi, const struct mipi_dsi_msg *msg, unsigned int count) { u8 *rx = msg->rx_buf; unsigned int i, j, k; size_t size = 0; u16 errors; u32 value; /* read and parse packet header */ value = tegra_dsi_readl(dsi, DSI_RD_DATA); switch (value & 0x3f) { case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: errors = (value >> 8) & 0xffff; printk(BIOS_ERR, "Acknowledge and error report: %04x\n", errors); for (i = 0; i < ARRAY_SIZE(error_report); i++) if (errors & BIT(i)) printk(BIOS_INFO, " %2u: %s\n", i, error_report[i]); break; case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: rx[0] = (value >> 8) & 0xff; break; case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: rx[0] = (value >> 8) & 0xff; rx[1] = (value >> 16) & 0xff; break; case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); break; case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); break; default: printk(BIOS_ERR, "unhandled response type: %02x\n", value & 0x3f); break; } size = MIN(size, msg->rx_len); if (msg->rx_buf && size > 0) { for (i = 0, j = 0; i < count - 1; i++, j += 4) { value = tegra_dsi_readl(dsi, DSI_RD_DATA); for (k = 0; k < 4 && (j + k) < msg->rx_len; k++) rx[j + k] = (value >> (k << 3)) & 0xff; } } return 0; } static int tegra_dsi_transmit(struct tegra_dsi *dsi, unsigned long timeout_ms) { u32 poll_interval_us = 2000; u32 timeout_us = timeout_ms * 1000; tegra_dsi_writel(dsi, DSI_TRIGGER_HOST, DSI_TRIGGER); udelay(poll_interval_us); do { u32 value = tegra_dsi_readl(dsi, DSI_TRIGGER); if ((value & DSI_TRIGGER_HOST) == 0) return 0; //usleep_range(1000, 2000); if (timeout_us > poll_interval_us) timeout_us -= poll_interval_us; else break; udelay(poll_interval_us); } while (1); printk(BIOS_ERR, "%s: ERROR: timeout waiting for transmission" " to complete\n", __func__); return -ETIMEDOUT; } static int tegra_dsi_wait_for_response(struct tegra_dsi *dsi, unsigned long timeout_ms) { u32 poll_interval_us = 2000; u32 timeout_us = timeout_ms * 1000; do { u32 value = tegra_dsi_readl(dsi, DSI_STATUS); u8 count = value & 0x1f; if (count > 0) return count; if (timeout_us > poll_interval_us) timeout_us -= poll_interval_us; else break; udelay(poll_interval_us); } while (1); printk(BIOS_ERR, "%s: ERROR: timeout\n", __func__); return -ETIMEDOUT; } static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg) { struct tegra_dsi *dsi = host_to_tegra(host); const u8 *tx = msg->tx_buf; unsigned int count, i, j; u32 value; int err; if (msg->tx_len > dsi->video_fifo_depth * 4) return -ENOSPC; /* reset underflow/overflow flags */ value = tegra_dsi_readl(dsi, DSI_STATUS); if (value & (DSI_STATUS_UNDERFLOW | DSI_STATUS_OVERFLOW)) { value = DSI_HOST_CONTROL_FIFO_RESET; tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); udelay(20); // usleep_range(10, 20); } value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); value |= DSI_POWER_CONTROL_ENABLE; tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); udelay(7000); //usleep_range(5000, 10000); value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC; if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0) value |= DSI_HOST_CONTROL_HS; /* * The host FIFO has a maximum of 64 words, so larger transmissions * need to use the video FIFO. */ if (msg->tx_len > dsi->host_fifo_depth * 4) value |= DSI_HOST_CONTROL_FIFO_SEL; tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); /* * For reads and messages with explicitly requested ACK, generate a * BTA sequence after the transmission of the packet. */ if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || (msg->rx_buf && msg->rx_len > 0)) { value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL); value |= DSI_HOST_CONTROL_PKT_BTA; tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); } value = DSI_CONTROL_LANES(0) | DSI_CONTROL_HOST_ENABLE; tegra_dsi_writel(dsi, value, DSI_CONTROL); /* write packet header */ value = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f); if (tx && msg->tx_len > 0) value |= tx[0] << 8; if (tx && msg->tx_len > 1) value |= tx[1] << 16; tegra_dsi_writel(dsi, value, DSI_WR_DATA); /* write payload (if any) */ if (msg->tx_len > 2) { for (j = 2; j < msg->tx_len; j += 4) { value = 0; for (i = 0; i < 4 && j + i < msg->tx_len; i++) value |= tx[j + i] << (i << 3); tegra_dsi_writel(dsi, value, DSI_WR_DATA); } } err = tegra_dsi_transmit(dsi, 250); if (err < 0) { printk(BIOS_INFO, "Failed to transmit. %d\n", err); return err; } if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || (msg->rx_buf && msg->rx_len > 0)) { err = tegra_dsi_wait_for_response(dsi, 250); if (err < 0) { printk(BIOS_INFO, "Failed to read response. %d\n", err); return err; } count = err; value = tegra_dsi_readl(dsi, DSI_RD_DATA); switch (value) { case 0x84: printk(BIOS_INFO, "ACK\n"); break; case 0x87: printk(BIOS_INFO, "ESCAPE\n"); break; default: printk(BIOS_INFO, "unknown status: %08x\n", value); break; } if (count > 1) { err = tegra_dsi_read_response(dsi, msg, count); if (err < 0) printk(BIOS_INFO, "failed to parse response: %d\n", err); else { /* * For read commands, return the number of * bytes returned by the peripheral. */ count = err; } } } else { /* * For write commands, we have transmitted the 4-byte header * plus the variable-length payload. */ count = 4 + msg->tx_len; } return count; } static int tegra_dsi_ganged_setup(struct tegra_dsi *dsi, struct tegra_dsi *slave) { /* * The number of ganged lanes is the sum of lanes of all peripherals * in the gang. */ dsi->slave->ganged_lanes = dsi->lanes + dsi->slave->lanes; dsi->slave->ganged_mode = 1; dsi->ganged_lanes = dsi->lanes + dsi->slave->lanes; dsi->ganged_mode = 1; return 0; } static int tegra_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct tegra_dsi *dsi = host_to_tegra(host); int err; dsi->flags = device->mode_flags; dsi->format = device->format; dsi->lanes = device->lanes; if (dsi->master) { err = tegra_dsi_ganged_setup(dsi->master, dsi); if (err < 0) { printk(BIOS_ERR, "failed to set up ganged mode: %d\n", err); return err; } } return 0; } static const struct mipi_dsi_host_ops tegra_dsi_host_ops = { .attach = tegra_dsi_host_attach, .transfer = tegra_dsi_host_transfer, }; static int dsi_probe_if(int dsi_index, struct soc_nvidia_tegra210_config *config) { struct tegra_dsi *dsi = &dsi_data[dsi_index]; int err; tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL); /* * Set default value. Will be taken from attached device once detected */ dsi->flags = 0; dsi->format = MIPI_DSI_FMT_RGB888; dsi->lanes = 4; /* get tegra_mipi_device */ dsi->mipi = tegra_mipi_request(&mipi_device_data[dsi_index], dsi_index); /* calibrate */ err = tegra_dsi_pad_calibrate(dsi); if (err < 0) { printk(BIOS_ERR, "MIPI calibration failed: %d\n", err); return err; } dsi->host.ops = &tegra_dsi_host_ops; err = mipi_dsi_host_register(&dsi->host); if (err < 0) { printk(BIOS_ERR, "failed to register DSI host: %d\n", err); return err; } /* get panel */ dsi->panel = panel_jdi_dsi_probe((struct mipi_dsi_device *)dsi->host.dev); if (IS_ERR_PTR(dsi->panel)) { printk(BIOS_ERR, "failed to get dsi panel\n"); return -EPTR; } dsi->panel->mode = config; return 0; } static int dsi_probe(struct soc_nvidia_tegra210_config *config) { dsi_probe_if(DSI_A, config); dsi_probe_if(DSI_B, config); return 0; } static void tegra_dsi_init_regs(struct tegra_dsi *dsi) { int i; for (i = 0; i < ARRAY_SIZE(init_reg); i++) tegra_dsi_writel(dsi, 0, init_reg[i]); if (dsi->slave) tegra_dsi_init_regs(dsi->slave); } static int dsi_enable(struct soc_nvidia_tegra210_config *config) { struct tegra_dsi *dsi_a = &dsi_data[DSI_A]; dsi_probe(config); /* set up clock and TimeOutRegisters */ tegra_output_dsi_setup_clock(dsi_a, config); /* configure APB_MISC_GP_MIPI_PAD_CTRL_0 */ write32((unsigned int *)APB_MISC_GP_MIPI_PAD_CTRL_0, DSIB_MODE_DSI); /* configure phy interface timing registers */ tegra_dsi_set_phy_timing(dsi_a); /* Initialize DSI registers */ tegra_dsi_init_regs(dsi_a); /* prepare panel */ panel_jdi_prepare(dsi_a->panel); /* enable dsi */ if (tegra_output_dsi_enable(dsi_a, config)) { printk(BIOS_ERR,"%s: Error: failed to enable dsi output.\n", __func__); return -1; } return 0; } void dsi_display_startup(struct device *dev) { struct soc_nvidia_tegra210_config *config = dev->chip_info; struct display_controller *disp_ctrl = (void *)config->display_controller; u32 plld_rate; u32 framebuffer_size_mb = config->framebuffer_size / MiB; u32 framebuffer_base_mb= config->framebuffer_base / MiB; printk(BIOS_INFO, "%s: entry: disp_ctrl: %p.\n", __func__, disp_ctrl); if (disp_ctrl == NULL) { printk(BIOS_ERR, "Error: No dc is assigned by dt.\n"); return; } if (framebuffer_size_mb == 0){ framebuffer_size_mb = ALIGN_UP(config->display_xres * config->display_yres * (config->framebuffer_bits_per_pixel / 8), MiB)/MiB; } config->framebuffer_size = framebuffer_size_mb * MiB; config->framebuffer_base = framebuffer_base_mb * MiB; /* * The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the * update_display_mode() for detail. */ /* set default plld */ plld_rate = clock_configure_plld(config->pixel_clock * 2); if (plld_rate == 0) { printk(BIOS_ERR, "dc: clock init failed\n"); return; } /* set disp1's clock source to PLLD_OUT0 */ clock_configure_source(disp1, PLLD_OUT0, (plld_rate/KHz)); /* Init dc */ if (tegra_dc_init(disp_ctrl)) { printk(BIOS_ERR, "dc: init failed\n"); return; } /* Configure dc mode */ if (update_display_mode(disp_ctrl, config)) { printk(BIOS_ERR, "dc: failed to configure display mode.\n"); return; } /* Configure and enable dsi controller and panel */ if (dsi_enable(config)) { printk(BIOS_ERR, "%s: failed to enable dsi controllers.\n", __func__); return; } /* Set up window */ update_window(config); printk(BIOS_INFO, "%s: display init done.\n", __func__); /* Save panel information to cb tables */ pass_mode_info_to_payload(config); /* * After this point, it is payload's responsibility to allocate * framebuffer and sets the base address to dc's * WINBUF_START_ADDR register and enables window by setting dc's * DISP_DISP_WIN_OPTIONS register. */ }