// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2016 Nexell Co., Ltd. * * Author: junghyun, kim */ #include #include #include #include #include #include #include #include "soc/s5pxx18_soc_mipi.h" #include "soc/s5pxx18_soc_disptop.h" #include "soc/s5pxx18_soc_disptop_clk.h" #define PLLPMS_1000MHZ 0x33E8 #define BANDCTL_1000MHZ 0xF #define PLLPMS_960MHZ 0x2280 #define BANDCTL_960MHZ 0xF #define PLLPMS_900MHZ 0x2258 #define BANDCTL_900MHZ 0xE #define PLLPMS_840MHZ 0x2230 #define BANDCTL_840MHZ 0xD #define PLLPMS_750MHZ 0x43E8 #define BANDCTL_750MHZ 0xC #define PLLPMS_660MHZ 0x21B8 #define BANDCTL_660MHZ 0xB #define PLLPMS_600MHZ 0x2190 #define BANDCTL_600MHZ 0xA #define PLLPMS_540MHZ 0x2168 #define BANDCTL_540MHZ 0x9 #define PLLPMS_512MHZ 0x03200 #define BANDCTL_512MHZ 0x9 #define PLLPMS_480MHZ 0x2281 #define BANDCTL_480MHZ 0x8 #define PLLPMS_420MHZ 0x2231 #define BANDCTL_420MHZ 0x7 #define PLLPMS_402MHZ 0x2219 #define BANDCTL_402MHZ 0x7 #define PLLPMS_330MHZ 0x21B9 #define BANDCTL_330MHZ 0x6 #define PLLPMS_300MHZ 0x2191 #define BANDCTL_300MHZ 0x5 #define PLLPMS_210MHZ 0x2232 #define BANDCTL_210MHZ 0x4 #define PLLPMS_180MHZ 0x21E2 #define BANDCTL_180MHZ 0x3 #define PLLPMS_150MHZ 0x2192 #define BANDCTL_150MHZ 0x2 #define PLLPMS_100MHZ 0x3323 #define BANDCTL_100MHZ 0x1 #define PLLPMS_80MHZ 0x3283 #define BANDCTL_80MHZ 0x0 #define MIPI_INDEX 0 #define MIPI_EXC_PRE_VALUE 1 #define MIPI_DSI_IRQ_MASK 29 #define __io_address(a) (void *)(uintptr_t)(a) struct mipi_xfer_msg { u8 id, data[2]; u16 flags; const u8 *tx_buf; u16 tx_len; u8 *rx_buf; u16 rx_len; }; static void mipi_reset(void) { /* tieoff */ nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAA, 3); nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAB, 3); /* reset */ nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_ASSERT); nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_ASSERT); nx_rstcon_setrst(RESET_ID_MIPI_CSI, RSTCON_ASSERT); nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_ASSERT); nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_ASSERT); nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_NEGATE); nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_NEGATE); nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_NEGATE); nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_NEGATE); } static void mipi_init(void) { int clkid = DP_CLOCK_MIPI; void *base; /* * neet to reset before open */ mipi_reset(); base = __io_address(nx_disp_top_clkgen_get_physical_address(clkid)); nx_disp_top_clkgen_set_base_address(clkid, base); nx_disp_top_clkgen_set_clock_pclk_mode(clkid, nx_pclkmode_always); base = __io_address(nx_mipi_get_physical_address(0)); nx_mipi_set_base_address(0, base); } static int mipi_get_phy_pll(int bitrate, unsigned int *pllpms, unsigned int *bandctl) { unsigned int pms, ctl; switch (bitrate) { case 1000: pms = PLLPMS_1000MHZ; ctl = BANDCTL_1000MHZ; break; case 960: pms = PLLPMS_960MHZ; ctl = BANDCTL_960MHZ; break; case 900: pms = PLLPMS_900MHZ; ctl = BANDCTL_900MHZ; break; case 840: pms = PLLPMS_840MHZ; ctl = BANDCTL_840MHZ; break; case 750: pms = PLLPMS_750MHZ; ctl = BANDCTL_750MHZ; break; case 660: pms = PLLPMS_660MHZ; ctl = BANDCTL_660MHZ; break; case 600: pms = PLLPMS_600MHZ; ctl = BANDCTL_600MHZ; break; case 540: pms = PLLPMS_540MHZ; ctl = BANDCTL_540MHZ; break; case 512: pms = PLLPMS_512MHZ; ctl = BANDCTL_512MHZ; break; case 480: pms = PLLPMS_480MHZ; ctl = BANDCTL_480MHZ; break; case 420: pms = PLLPMS_420MHZ; ctl = BANDCTL_420MHZ; break; case 402: pms = PLLPMS_402MHZ; ctl = BANDCTL_402MHZ; break; case 330: pms = PLLPMS_330MHZ; ctl = BANDCTL_330MHZ; break; case 300: pms = PLLPMS_300MHZ; ctl = BANDCTL_300MHZ; break; case 210: pms = PLLPMS_210MHZ; ctl = BANDCTL_210MHZ; break; case 180: pms = PLLPMS_180MHZ; ctl = BANDCTL_180MHZ; break; case 150: pms = PLLPMS_150MHZ; ctl = BANDCTL_150MHZ; break; case 100: pms = PLLPMS_100MHZ; ctl = BANDCTL_100MHZ; break; case 80: pms = PLLPMS_80MHZ; ctl = BANDCTL_80MHZ; break; default: return -EINVAL; } *pllpms = pms; *bandctl = ctl; return 0; } static int mipi_prepare(int module, int input, struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, struct dp_mipi_dev *mipi) { int index = MIPI_INDEX; u32 esc_pre_value = MIPI_EXC_PRE_VALUE; int lpm = mipi->lpm_trans; int ret = 0; ret = mipi_get_phy_pll(mipi->hs_bitrate, &mipi->hs_pllpms, &mipi->hs_bandctl); if (ret < 0) return ret; ret = mipi_get_phy_pll(mipi->lp_bitrate, &mipi->lp_pllpms, &mipi->lp_bandctl); if (ret < 0) return ret; debug("%s: mipi lp:%dmhz:0x%x:0x%x, hs:%dmhz:0x%x:0x%x, %s trans\n", __func__, mipi->lp_bitrate, mipi->lp_pllpms, mipi->lp_bandctl, mipi->hs_bitrate, mipi->hs_pllpms, mipi->hs_bandctl, lpm ? "low" : "high"); if (lpm) nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF, mipi->lp_pllpms, mipi->lp_bandctl, 0, 0); else nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF, mipi->hs_pllpms, mipi->hs_bandctl, 0, 0); #ifdef CONFIG_ARCH_S5P4418 /* * disable the escape clock generating prescaler * before soft reset. */ nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 0, 10); mdelay(1); #endif nx_mipi_dsi_software_reset(index); nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 1, esc_pre_value); nx_mipi_dsi_set_phy(index, 0, 1, 1, 0, 0, 0, 0, 0); if (lpm) nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_lp, nx_mipi_dsi_lpmode_lp); else nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_hs, nx_mipi_dsi_lpmode_hs); mdelay(20); return 0; } static int mipi_enable(int module, int input, struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, struct dp_mipi_dev *mipi) { struct mipi_dsi_device *dsi = &mipi->dsi; int clkid = DP_CLOCK_MIPI; int index = MIPI_INDEX; int width = sync->h_active_len; int height = sync->v_active_len; int HFP = sync->h_front_porch; int HBP = sync->h_back_porch; int HS = sync->h_sync_width; int VFP = sync->v_front_porch; int VBP = sync->v_back_porch; int VS = sync->v_sync_width; int en_prescaler = 1; u32 esc_pre_value = MIPI_EXC_PRE_VALUE; int txhsclock = 1; int lpm = mipi->lpm_trans; bool command_mode = mipi->command_mode; enum nx_mipi_dsi_format dsi_format; int data_len = dsi->lanes - 1; bool burst = dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST ? true : false; bool eot_enable = dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET ? false : true; /* * disable the escape clock generating prescaler * before soft reset. */ #ifdef CONFIG_ARCH_S5P4418 en_prescaler = 0; #endif debug("%s: mode:%s, lanes.%d\n", __func__, command_mode ? "command" : "video", data_len + 1); if (lpm) nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_hs, nx_mipi_dsi_lpmode_hs); nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF, mipi->hs_pllpms, mipi->hs_bandctl, 0, 0); mdelay(1); nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, en_prescaler, 10); mdelay(1); nx_mipi_dsi_software_reset(index); nx_mipi_dsi_set_clock(index, txhsclock, 0, 1, 1, 1, 0, 0, 0, 1, esc_pre_value); switch (data_len) { case 0: /* 1 lane */ nx_mipi_dsi_set_phy(index, data_len, 1, 1, 0, 0, 0, 0, 0); break; case 1: /* 2 lane */ nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 0, 0, 0, 0); break; case 2: /* 3 lane */ nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 0, 0, 0); break; case 3: /* 3 lane */ nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 1, 0, 0); break; default: printf("%s: not support data lanes %d\n", __func__, data_len + 1); return -EINVAL; } switch (dsi->format) { case MIPI_DSI_FMT_RGB565: dsi_format = nx_mipi_dsi_format_rgb565; break; case MIPI_DSI_FMT_RGB666: dsi_format = nx_mipi_dsi_format_rgb666; break; case MIPI_DSI_FMT_RGB666_PACKED: dsi_format = nx_mipi_dsi_format_rgb666_packed; break; case MIPI_DSI_FMT_RGB888: dsi_format = nx_mipi_dsi_format_rgb888; break; default: printf("%s: not support format %d\n", __func__, dsi->format); return -EINVAL; } nx_mipi_dsi_set_config_video_mode(index, 1, 0, burst, nx_mipi_dsi_syncmode_event, eot_enable, 1, 1, 1, 1, 0, dsi_format, HFP, HBP, HS, VFP, VBP, VS, 0); nx_mipi_dsi_set_size(index, width, height); /* set mux */ nx_disp_top_set_mipimux(1, module); /* 0 is spdif, 1 is mipi vclk */ nx_disp_top_clkgen_set_clock_source(clkid, 1, ctrl->clk_src_lv0); nx_disp_top_clkgen_set_clock_divisor(clkid, 1, ctrl->clk_div_lv1 * ctrl->clk_div_lv0); /* SPDIF and MIPI */ nx_disp_top_clkgen_set_clock_divisor_enable(clkid, 1); /* START: CLKGEN, MIPI is started in setup function */ nx_disp_top_clkgen_set_clock_divisor_enable(clkid, true); nx_mipi_dsi_set_enable(index, true); return 0; } static int nx_mipi_transfer_tx(struct mipi_dsi_device *dsi, struct mipi_xfer_msg *xfer) { const u8 *txb; int size, index = 0; u32 data; if (xfer->tx_len > DSI_TX_FIFO_SIZE) printf("warn: tx %d size over fifo %d\n", (int)xfer->tx_len, DSI_TX_FIFO_SIZE); /* write payload */ size = xfer->tx_len; txb = xfer->tx_buf; while (size >= 4) { data = (txb[3] << 24) | (txb[2] << 16) | (txb[1] << 8) | (txb[0]); nx_mipi_dsi_write_payload(index, data); txb += 4, size -= 4; data = 0; } switch (size) { case 3: data |= txb[2] << 16; case 2: data |= txb[1] << 8; case 1: data |= txb[0]; nx_mipi_dsi_write_payload(index, data); break; case 0: break; /* no payload */ } /* write packet hdr */ data = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->id; nx_mipi_dsi_write_pkheader(index, data); return 0; } static int nx_mipi_transfer_done(struct mipi_dsi_device *dsi) { int index = 0, count = 100; u32 value; do { mdelay(1); value = nx_mipi_dsi_read_fifo_status(index); if (((1 << 22) & value)) break; } while (count-- > 0); if (count < 0) return -EINVAL; return 0; } static int nx_mipi_transfer_rx(struct mipi_dsi_device *dsi, struct mipi_xfer_msg *xfer) { u8 *rxb = xfer->rx_buf; int index = 0, rx_len = 0; u32 data, count = 0; u16 size; int err = -EINVAL; nx_mipi_dsi_clear_interrupt_pending(index, 18); while (1) { /* Completes receiving data. */ if (nx_mipi_dsi_get_interrupt_pending(index, 18)) break; mdelay(1); if (count > 500) { printf("%s: error recevice data\n", __func__); err = -EINVAL; goto clear_fifo; } else { count++; } } data = nx_mipi_dsi_read_fifo(index); switch (data & 0x3f) { case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: if (xfer->rx_len >= 2) { rxb[1] = data >> 16; rx_len++; } /* Fall through */ case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: rxb[0] = data >> 8; rx_len++; xfer->rx_len = rx_len; err = rx_len; goto clear_fifo; case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: printf("DSI Error Report: 0x%04x\n", (data >> 8) & 0xffff); err = rx_len; goto clear_fifo; } size = (data >> 8) & 0xffff; if (size > xfer->rx_len) size = xfer->rx_len; else if (size < xfer->rx_len) xfer->rx_len = size; size = xfer->rx_len - rx_len; rx_len += size; /* Receive payload */ while (size >= 4) { data = nx_mipi_dsi_read_fifo(index); rxb[0] = (data >> 0) & 0xff; rxb[1] = (data >> 8) & 0xff; rxb[2] = (data >> 16) & 0xff; rxb[3] = (data >> 24) & 0xff; rxb += 4, size -= 4; } if (size) { data = nx_mipi_dsi_read_fifo(index); switch (size) { case 3: rxb[2] = (data >> 16) & 0xff; case 2: rxb[1] = (data >> 8) & 0xff; case 1: rxb[0] = data & 0xff; } } if (rx_len == xfer->rx_len) err = rx_len; clear_fifo: size = DSI_RX_FIFO_SIZE / 4; do { data = nx_mipi_dsi_read_fifo(index); if (data == DSI_RX_FIFO_EMPTY) break; } while (--size); return err; } #define IS_SHORT(t) (9 > ((t) & 0x0f)) static int nx_mipi_transfer(struct mipi_dsi_device *dsi, const struct mipi_dsi_msg *msg) { struct mipi_xfer_msg xfer; int err; if (!msg->tx_len) return -EINVAL; /* set id */ xfer.id = msg->type | (msg->channel << 6); /* short type msg */ if (IS_SHORT(msg->type)) { const char *txb = msg->tx_buf; if (msg->tx_len > 2) return -EINVAL; xfer.tx_len = 0; /* no payload */ xfer.data[0] = txb[0]; xfer.data[1] = (msg->tx_len == 2) ? txb[1] : 0; xfer.tx_buf = NULL; } else { xfer.tx_len = msg->tx_len; xfer.data[0] = msg->tx_len & 0xff; xfer.data[1] = msg->tx_len >> 8; xfer.tx_buf = msg->tx_buf; } xfer.rx_len = msg->rx_len; xfer.rx_buf = msg->rx_buf; xfer.flags = msg->flags; err = nx_mipi_transfer_tx(dsi, &xfer); if (xfer.rx_len) err = nx_mipi_transfer_rx(dsi, &xfer); nx_mipi_transfer_done(dsi); return err; } static ssize_t nx_mipi_write_buffer(struct mipi_dsi_device *dsi, const void *data, size_t len) { struct mipi_dsi_msg msg = { .channel = dsi->channel, .tx_buf = data, .tx_len = len }; switch (len) { case 0: return -EINVAL; case 1: msg.type = MIPI_DSI_DCS_SHORT_WRITE; break; case 2: msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM; break; default: msg.type = MIPI_DSI_DCS_LONG_WRITE; break; } if (dsi->mode_flags & MIPI_DSI_MODE_LPM) msg.flags |= MIPI_DSI_MSG_USE_LPM; return nx_mipi_transfer(dsi, &msg); } __weak int nx_mipi_dsi_lcd_bind(struct mipi_dsi_device *dsi) { return 0; } /* * disply * MIPI DSI Setting * (1) Initiallize MIPI(DSIM,DPHY,PLL) * (2) Initiallize LCD * (3) ReInitiallize MIPI(DSIM only) * (4) Turn on display(MLC,DPC,...) */ void nx_mipi_display(int module, struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, struct dp_plane_top *top, struct dp_plane_info *planes, struct dp_mipi_dev *dev) { struct dp_plane_info *plane = planes; struct mipi_dsi_device *dsi = &dev->dsi; int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1; int count = top->plane_num; int i = 0, ret; printf("MIPI: dp.%d\n", module); /* map mipi-dsi write callback func */ dsi->write_buffer = nx_mipi_write_buffer; ret = nx_mipi_dsi_lcd_bind(dsi); if (ret) { printf("Error: bind mipi-dsi lcd driver !\n"); return; } dp_control_init(module); dp_plane_init(module); mipi_init(); /* set plane */ dp_plane_screen_setup(module, top); for (i = 0; count > i; i++, plane++) { if (!plane->enable) continue; dp_plane_layer_setup(module, plane); dp_plane_layer_enable(module, plane, 1); } dp_plane_screen_enable(module, 1); /* set mipi */ mipi_prepare(module, input, sync, ctrl, dev); if (dsi->ops && dsi->ops->prepare) dsi->ops->prepare(dsi); if (dsi->ops && dsi->ops->enable) dsi->ops->enable(dsi); mipi_enable(module, input, sync, ctrl, dev); /* set dp control */ dp_control_setup(module, sync, ctrl); dp_control_enable(module, 1); }