/* * From DSI6G(v3), addition of a 6G_HW_VERSION register at offset 0 * makes all other registers 4-byte shifted down. * * In order to identify between DSI6G(v3) and beyond, and DSIv2 and * older, we read the DSI_VERSION register without any shift(offset * 0x1f0). In the case of DSIv2, this hast to be a non-zero value. In * the case of DSI6G, this has to be zero (the offset points to a * scratch register which we never touch)
*/
ver = readl(base + REG_DSI_VERSION); if (ver) { /* older dsi host, there is no register shift */
ver = FIELD(ver, DSI_VERSION_MAJOR); if (ver <= MSM_DSI_VER_MAJOR_V2) { /* old versions */
*major = ver;
*minor = 0; return 0;
} else { return -EINVAL;
}
} else { /* * newer host, offset 0 has 6G_HW_VERSION, the rest of the * registers are shifted down, read DSI_VERSION again with * the shifted offset
*/
ver = readl(base + DSI_6G_REG_SHIFT + REG_DSI_VERSION);
ver = FIELD(ver, DSI_VERSION_MAJOR); if (ver == MSM_DSI_VER_MAJOR_6G) { /* 6G version */
*major = ver;
*minor = readl(base + REG_DSI_6G_HW_VERSION); return 0;
} else { return -EINVAL;
}
}
}
int dsi_clk_init_v2(struct msm_dsi_host *msm_host)
{ struct platform_device *pdev = msm_host->pdev; int ret = 0;
msm_host->src_clk = msm_clk_get(pdev, "src");
if (IS_ERR(msm_host->src_clk)) {
ret = PTR_ERR(msm_host->src_clk);
pr_err("%s: can't find src clock. ret=%d\n",
__func__, ret);
msm_host->src_clk = NULL; return ret;
}
return ret;
}
int dsi_clk_init_6g_v2(struct msm_dsi_host *msm_host)
{ struct platform_device *pdev = msm_host->pdev; int ret = 0;
msm_host->byte_intf_clk = msm_clk_get(pdev, "byte_intf"); if (IS_ERR(msm_host->byte_intf_clk)) {
ret = PTR_ERR(msm_host->byte_intf_clk);
pr_err("%s: can't find byte_intf clock. ret=%d\n",
__func__, ret);
}
return ret;
}
int dsi_clk_init_6g_v2_9(struct msm_dsi_host *msm_host)
{ struct device *dev = &msm_host->pdev->dev; int ret;
ret = dsi_clk_init_6g_v2(msm_host); if (ret) return ret;
msm_host->byte_src_clk = devm_clk_get(dev, "byte_src"); if (IS_ERR(msm_host->byte_src_clk)) return dev_err_probe(dev, PTR_ERR(msm_host->byte_src_clk), "can't get byte_src clock\n");
msm_host->dsi_pll_byte_clk = devm_clk_get(dev, "dsi_pll_byte"); if (IS_ERR(msm_host->dsi_pll_byte_clk)) return dev_err_probe(dev, PTR_ERR(msm_host->dsi_pll_byte_clk), "can't get dsi_pll_byte clock\n");
msm_host->pixel_src_clk = devm_clk_get(dev, "pixel_src"); if (IS_ERR(msm_host->pixel_src_clk)) return dev_err_probe(dev, PTR_ERR(msm_host->pixel_src_clk), "can't get pixel_src clock\n");
msm_host->dsi_pll_pixel_clk = devm_clk_get(dev, "dsi_pll_pixel"); if (IS_ERR(msm_host->dsi_pll_pixel_clk)) return dev_err_probe(dev, PTR_ERR(msm_host->dsi_pll_pixel_clk), "can't get dsi_pll_pixel clock\n");
return 0;
}
staticint dsi_clk_init(struct msm_dsi_host *msm_host)
{ struct platform_device *pdev = msm_host->pdev; conststruct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; conststruct msm_dsi_config *cfg = cfg_hnd->cfg; int i, ret = 0;
/* get bus clocks */ for (i = 0; i < cfg->num_bus_clks; i++)
msm_host->bus_clks[i].id = cfg->bus_clk_names[i];
msm_host->num_bus_clks = cfg->num_bus_clks;
ret = devm_clk_bulk_get(&pdev->dev, msm_host->num_bus_clks, msm_host->bus_clks); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "Unable to get clocks\n");
/* get link and source clocks */
msm_host->byte_clk = msm_clk_get(pdev, "byte"); if (IS_ERR(msm_host->byte_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->byte_clk), "%s: can't find dsi_byte clock\n",
__func__);
ret = dev_pm_opp_set_rate(&msm_host->pdev->dev,
msm_host->byte_clk_rate); if (ret) {
pr_err("%s: dev_pm_opp_set_rate failed %d\n", __func__, ret); return ret;
}
ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); if (ret) {
pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); return ret;
}
if (msm_host->byte_intf_clk) {
ret = clk_set_rate(msm_host->byte_intf_clk, msm_host->byte_intf_clk_rate); if (ret) {
pr_err("%s: Failed to set rate byte intf clk, %d\n",
__func__, ret); return ret;
}
}
return 0;
}
int dsi_link_clk_set_rate_6g_v2_9(struct msm_dsi_host *msm_host)
{ struct device *dev = &msm_host->pdev->dev; int ret;
/* * DSI PHY PLLs have to be enabled to allow reparenting to them, so * cannot use assigned-clock-parents.
*/
ret = clk_set_parent(msm_host->byte_src_clk, msm_host->dsi_pll_byte_clk); if (ret)
dev_err(dev, "Failed to parent byte_src -> dsi_pll_byte: %d\n", ret);
ret = clk_set_parent(msm_host->pixel_src_clk, msm_host->dsi_pll_pixel_clk); if (ret)
dev_err(dev, "Failed to parent pixel_src -> dsi_pll_pixel: %d\n", ret);
return dsi_link_clk_set_rate_6g(msm_host);
}
int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host)
{ int ret;
ret = clk_prepare_enable(msm_host->esc_clk); if (ret) {
pr_err("%s: Failed to enable dsi esc clk\n", __func__); goto error;
}
ret = clk_prepare_enable(msm_host->byte_clk); if (ret) {
pr_err("%s: Failed to enable dsi byte clk\n", __func__); goto byte_clk_err;
}
ret = clk_prepare_enable(msm_host->pixel_clk); if (ret) {
pr_err("%s: Failed to enable dsi pixel clk\n", __func__); goto pixel_clk_err;
}
ret = clk_prepare_enable(msm_host->byte_intf_clk); if (ret) {
pr_err("%s: Failed to enable byte intf clk\n",
__func__); goto byte_intf_clk_err;
}
ret = clk_set_rate(msm_host->byte_clk, msm_host->byte_clk_rate); if (ret) {
pr_err("%s: Failed to set rate byte clk, %d\n", __func__, ret); return ret;
}
ret = clk_set_rate(msm_host->esc_clk, msm_host->esc_clk_rate); if (ret) {
pr_err("%s: Failed to set rate esc clk, %d\n", __func__, ret); return ret;
}
ret = clk_set_rate(msm_host->src_clk, msm_host->src_clk_rate); if (ret) {
pr_err("%s: Failed to set rate src clk, %d\n", __func__, ret); return ret;
}
ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); if (ret) {
pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); return ret;
}
return 0;
}
int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host)
{ int ret;
ret = clk_prepare_enable(msm_host->byte_clk); if (ret) {
pr_err("%s: Failed to enable dsi byte clk\n", __func__); goto error;
}
ret = clk_prepare_enable(msm_host->esc_clk); if (ret) {
pr_err("%s: Failed to enable dsi esc clk\n", __func__); goto esc_clk_err;
}
ret = clk_prepare_enable(msm_host->src_clk); if (ret) {
pr_err("%s: Failed to enable dsi src clk\n", __func__); goto src_clk_err;
}
ret = clk_prepare_enable(msm_host->pixel_clk); if (ret) {
pr_err("%s: Failed to enable dsi pixel clk\n", __func__); goto pixel_clk_err;
}
/** * dsi_adjust_pclk_for_compression() - Adjust the pclk rate for compression case * @mode: The selected mode for the DSI output * @dsc: DRM DSC configuration for this DSI output * * Adjust the pclk rate by calculating a new hdisplay proportional to * the compression ratio such that: * new_hdisplay = old_hdisplay * compressed_bpp / uncompressed_bpp * * Porches do not need to be adjusted: * - For VIDEO mode they are not compressed by DSC and are passed as is. * - For CMD mode there are no actual porches. Instead these fields * currently represent the overhead to the image data transfer. As such, they * are calculated for the final mode parameters (after the compression) and * are not to be adjusted too. * * FIXME: Reconsider this if/when CMD mode handling is rewritten to use * transfer time and data overhead as a starting point of the calculations.
*/ staticunsignedlong dsi_adjust_pclk_for_compression(conststruct drm_display_mode *mode, conststruct drm_dsc_config *dsc)
{ int new_hdisplay = DIV_ROUND_UP(mode->hdisplay * drm_dsc_get_bpp_int(dsc),
dsc->bits_per_component * 3);
int new_htotal = mode->htotal - mode->hdisplay + new_hdisplay;
if (dsc)
pclk_rate = dsi_adjust_pclk_for_compression(mode, dsc);
/* * For bonded DSI mode, the current DRM mode has the complete width of the * panel. Since, the complete panel is driven by two DSI controllers, * the clock rates have to be split between the two dsi controllers. * Adjust the byte and pixel clock rates for each dsi host accordingly.
*/ if (is_bonded_dsi)
pclk_rate /= 2;
/* * esc clock is byte clock followed by a 4 bit divider, * we need to find an escape clock frequency within the * mipi DSI spec range within the maximum divider limit * We iterate here between an escape clock frequencey * between 20 Mhz to 5 Mhz and pick up the first one * that can be supported by our divider
*/
/* * TODO: Ideally, we shouldn't know what sort of divider * is available in mmss_cc, we're just assuming that * it'll always be a 4 bit divider. Need to come up with * a better way here.
*/ if (esc_div >= 1 && esc_div <= 16) break;
}
if (flags & MIPI_DSI_MODE_VIDEO) { if (flags & MIPI_DSI_MODE_VIDEO_HSE)
data |= DSI_VID_CFG0_PULSE_MODE_HSA_HE; if (flags & MIPI_DSI_MODE_VIDEO_NO_HFP)
data |= DSI_VID_CFG0_HFP_POWER_STOP; if (flags & MIPI_DSI_MODE_VIDEO_NO_HBP)
data |= DSI_VID_CFG0_HBP_POWER_STOP; if (flags & MIPI_DSI_MODE_VIDEO_NO_HSA)
data |= DSI_VID_CFG0_HSA_POWER_STOP; /* Always set low power stop mode for BLLP * to let command engine send packets
*/
data |= DSI_VID_CFG0_EOF_BLLP_POWER_STOP |
DSI_VID_CFG0_BLLP_POWER_STOP;
data |= DSI_VID_CFG0_TRAFFIC_MODE(dsi_get_traffic_mode(flags));
data |= DSI_VID_CFG0_DST_FORMAT(dsi_get_vid_fmt(mipi_fmt));
data |= DSI_VID_CFG0_VIRT_CHANNEL(msm_host->channel); if (msm_dsi_host_is_wide_bus_enabled(&msm_host->base))
data |= DSI_VID_CFG0_DATABUS_WIDEN;
dsi_write(msm_host, REG_DSI_VID_CFG0, data);
/* Do not swap RGB colors */
data = DSI_VID_CFG1_RGB_SWAP(SWAP_RGB);
dsi_write(msm_host, REG_DSI_VID_CFG1, 0);
} else { /* Do not swap RGB colors */
data = DSI_CMD_CFG0_RGB_SWAP(SWAP_RGB);
data |= DSI_CMD_CFG0_DST_FORMAT(dsi_get_cmd_fmt(mipi_fmt));
dsi_write(msm_host, REG_DSI_CMD_CFG0, data);
data = DSI_CMD_CFG1_WR_MEM_START(MIPI_DCS_WRITE_MEMORY_START) |
DSI_CMD_CFG1_WR_MEM_CONTINUE(
MIPI_DCS_WRITE_MEMORY_CONTINUE); /* Always insert DCS command */
data |= DSI_CMD_CFG1_INSERT_DCS_COMMAND;
dsi_write(msm_host, REG_DSI_CMD_CFG1, data);
if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) {
data = dsi_read(msm_host, REG_DSI_CMD_MODE_MDP_CTRL2);
if (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_3)
data |= DSI_CMD_MODE_MDP_CTRL2_BURST_MODE;
if (msm_dsi_host_is_wide_bus_enabled(&msm_host->base))
data |= DSI_CMD_MODE_MDP_CTRL2_DATABUS_WIDEN;
data = 0; /* Always assume dedicated TE pin */
data |= DSI_TRIG_CTRL_TE;
data |= DSI_TRIG_CTRL_MDP_TRIGGER(TRIGGER_NONE);
data |= DSI_TRIG_CTRL_DMA_TRIGGER(TRIGGER_SW);
data |= DSI_TRIG_CTRL_STREAM(msm_host->channel); if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) &&
(cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_2))
data |= DSI_TRIG_CTRL_BLOCK_DMA_WITHIN_FRAME;
dsi_write(msm_host, REG_DSI_TRIG_CTRL, data);
data = DSI_CLKOUT_TIMING_CTRL_T_CLK_POST(phy_shared_timings->clk_post) |
DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE(phy_shared_timings->clk_pre);
dsi_write(msm_host, REG_DSI_CLKOUT_TIMING_CTRL, data);
/* * Typically, pkt_per_line = slice_per_intf * slice_per_pkt. * * Since the current driver only supports slice_per_pkt = 1, * pkt_per_line will be equal to slice per intf for now.
*/
pkt_per_line = slice_per_intf;
if (is_cmd_mode) /* packet data type */
reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE); else
reg = DSI_VIDEO_COMPRESSION_MODE_CTRL_DATATYPE(MIPI_DSI_COMPRESSED_PIXEL_STREAM);
/* DSI_VIDEO_COMPRESSION_MODE & DSI_COMMAND_COMPRESSION_MODE * registers have similar offsets, so for below common code use * DSI_VIDEO_COMPRESSION_MODE_XXXX for setting bits * * pkt_per_line is log2 encoded, >>1 works for supported values (1,2,4)
*/ if (pkt_per_line > 4)
drm_warn_once(msm_host->dev, "pkt_per_line too big");
reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_PKT_PER_LINE(pkt_per_line >> 1);
reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_EOL_BYTE_NUM(eol_byte_num);
reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_EN;
/* * For bonded DSI mode, the current DRM mode has * the complete width of the panel. Since, the complete * panel is driven by two DSI controllers, the horizontal * timings have to be split between the two dsi controllers. * Adjust the DSI host timing values accordingly.
*/ if (is_bonded_dsi) {
h_total /= 2;
hs_end /= 2;
ha_start /= 2;
ha_end /= 2;
hdisplay /= 2;
}
if (msm_host->dsc) { struct drm_dsc_config *dsc = msm_host->dsc;
u32 bytes_per_pclk;
/* we do the calculations for dsc parameters here so that * panel can use these parameters
*/
ret = dsi_populate_dsc_params(msm_host, dsc); if (ret) return;
/* * DPU sends 3 bytes per pclk cycle to DSI. If widebus is * enabled, bus width is extended to 6 bytes. * * Calculate the number of pclks needed to transmit one line of * the compressed data.
* The back/font porch and pulse width are kept intact. For * VIDEO mode they represent timing parameters rather than * actual data transfer, see the documentation for * dsi_adjust_pclk_for_compression(). For CMD mode they are * unused anyway.
*/
h_total -= hdisplay; if (wide_bus_enabled && !(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO))
bytes_per_pclk = 6; else
bytes_per_pclk = 3;
/* image data and 1 byte write_memory_start cmd */ if (!msm_host->dsc)
wc = hdisplay * mipi_dsi_pixel_format_to_bpp(msm_host->format) / 8 + 1; else /* * When DSC is enabled, WC = slice_chunk_size * slice_per_pkt + 1. * Currently, the driver only supports default value of slice_per_pkt = 1 * * TODO: Expand mipi_dsi_device struct to hold slice_per_pkt info * and adjust DSC math to account for slice_per_pkt.
*/
wc = msm_host->dsc->slice_chunk_size + 1;
if (ctrl & DSI_CTRL_ENABLE) {
dsi_write(msm_host, REG_DSI_CTRL, ctrl & ~DSI_CTRL_ENABLE); /* * dsi controller need to be disabled before * clocks turned on
*/
wmb();
}
dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS);
wmb(); /* clocks need to be enabled before reset */
/* dsi controller can only be reset while clocks are running */
dsi_write(msm_host, REG_DSI_RESET, 1);
msleep(DSI_RESET_TOGGLE_DELAY_MS); /* make sure reset happen */
dsi_write(msm_host, REG_DSI_RESET, 0);
wmb(); /* controller out of reset */
if (ctrl & DSI_CTRL_ENABLE) {
dsi_write(msm_host, REG_DSI_CTRL, ctrl);
wmb(); /* make sure dsi controller enabled again */
}
}
if (!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO)) return;
data = dsi_read(msm_host, REG_DSI_STATUS0);
/* if video mode engine is not busy, its because * either timing engine was not turned on or the * DSI controller has finished transmitting the video * data already, so no need to wait in those cases
*/ if (!(data & DSI_STATUS0_VIDEO_MODE_ENGINE_BUSY)) return;
if (msm_host->power_on && msm_host->enabled) {
dsi_wait4video_done(msm_host); /* delay 4 ms to skip BLLP */
usleep_range(2000, 4000);
}
}
/* * This is possible if we're tearing down before we've had a chance to * fully initialize. A very real possibility if our probe is deferred, * in which case we'll hit msm_dsi_host_destroy() without having run * through the dsi_tx_buf_alloc().
*/ if (!dev) return;
/* * In case of multiple reads from the panel, after the first read, there * is possibility that there are some bytes in the payload repeating in * the RDBK_DATA registers. Since we read all the parameters from the * panel right from the first byte for every pass. We need to skip the * repeating bytes and then append the new parameters to the rx buffer.
*/ if (read_cnt > 16) { int bytes_shifted; /* Any data more than 16 bytes will be shifted out. * The temp read buffer should already contain these bytes. * The remaining bytes in read buffer are the repeated bytes.
*/
bytes_shifted = read_cnt - 16;
repeated_bytes = buf_offset - bytes_shifted;
}
for (i = cnt - 1; i >= 0; i--) {
data = dsi_read(msm_host, REG_DSI_RDBK_DATA(i));
*temp++ = ntohl(data); /* to host byte order */
DBG("data = 0x%x and ntohl(data) = 0x%x", data, ntohl(data));
}
for (i = repeated_bytes; i < 16; i++)
buf[j++] = reg[i];
return j;
}
staticint dsi_cmds2buf_tx(struct msm_dsi_host *msm_host, conststruct mipi_dsi_msg *msg)
{ int len, ret; int bllp_len = msm_host->mode->hdisplay *
mipi_dsi_pixel_format_to_bpp(msm_host->format) / 8;
len = dsi_cmd_dma_add(msm_host, msg); if (len < 0) {
pr_err("%s: failed to add cmd type = 0x%x\n",
__func__, msg->type); return len;
}
/* * for video mode, do not send cmds more than * one pixel line, since it only transmit it * during BLLP. * * TODO: if the command is sent in LP mode, the bit rate is only * half of esc clk rate. In this case, if the video is already * actively streaming, we need to check more carefully if the * command can be fit into one BLLP.
*/ if ((msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) && (len > bllp_len)) {
pr_err("%s: cmd cannot fit into BLLP period, len=%d\n",
__func__, len); return -EINVAL;
}
prop = of_find_property(ep, "data-lanes", &len); if (!prop) {
DRM_DEV_DEBUG(dev, "failed to find data lane mapping, using default\n"); /* Set the number of date lanes to 4 by default. */
msm_host->num_data_lanes = 4; return 0;
}
num_lanes = drm_of_get_data_lanes_count(ep, 1, 4); if (num_lanes < 0) {
DRM_DEV_ERROR(dev, "bad number of data lanes\n"); return num_lanes;
}
msm_host->num_data_lanes = num_lanes;
ret = of_property_read_u32_array(ep, "data-lanes", lane_map,
num_lanes); if (ret) {
DRM_DEV_ERROR(dev, "failed to read lane data\n"); return ret;
}
/* * compare DT specified physical-logical lane mappings with the ones * supported by hardware
*/ for (i = 0; i < ARRAY_SIZE(supported_data_lane_swaps); i++) { constint *swap = supported_data_lane_swaps[i]; int j;
/* * the data-lanes array we get from DT has a logical->physical * mapping. The "data lane swap" register field represents * supported configurations in a physical->logical mapping. * Translate the DT mapping to what we understand and find a * configuration that works.
*/ for (j = 0; j < num_lanes; j++) { if (lane_map[j] < 0 || lane_map[j] > 3)
DRM_DEV_ERROR(dev, "bad physical lane entry %u\n",
lane_map[j]);
staticint dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc)
{ int ret;
if (dsc->bits_per_pixel & 0xf) {
DRM_DEV_ERROR(&msm_host->pdev->dev, "DSI does not support fractional bits_per_pixel\n"); return -EINVAL;
}
switch (dsc->bits_per_component) { case 8: case 10: case 12: /* * Only 8, 10, and 12 bpc are supported for DSC 1.1 block. * If additional bpc values need to be supported, update * this quard with the appropriate DSC version verification.
*/ break; default:
DRM_DEV_ERROR(&msm_host->pdev->dev, "Unsupported bits_per_component value: %d\n",
dsc->bits_per_component); return -EOPNOTSUPP;
}
/* * Get the endpoint of the output port of the DSI host. In our case, * this is mapped to port number with reg = 1. Don't return an error if * the remote endpoint isn't defined. It's possible that there is * nothing connected to the dsi output.
*/
endpoint = of_graph_get_endpoint_by_regs(np, 1, -1); if (!endpoint) {
DRM_DEV_DEBUG(dev, "%s: no endpoint\n", __func__); return 0;
}
ret = dsi_host_parse_lane_data(msm_host, endpoint); if (ret) {
DRM_DEV_ERROR(dev, "%s: invalid lane configuration %d\n",
__func__, ret);
ret = -EINVAL; goto err;
}
ret = of_property_read_string(endpoint, "qcom,te-source", &te_source); if (ret && ret != -EINVAL) {
DRM_DEV_ERROR(dev, "%s: invalid TE source configuration %d\n",
__func__, ret); goto err;
} if (!ret) {
msm_dsi->te_source = devm_kstrdup(dev, te_source, GFP_KERNEL); if (!msm_dsi->te_source) {
DRM_DEV_ERROR(dev, "%s: failed to allocate te_source\n",
__func__);
ret = -ENOMEM; goto err;
}
}
ret = 0;
if (of_property_present(np, "syscon-sfpb")) {
msm_host->sfpb = syscon_regmap_lookup_by_phandle(np, "syscon-sfpb"); if (IS_ERR(msm_host->sfpb)) {
DRM_DEV_ERROR(dev, "%s: failed to get sfpb regmap\n",
__func__);
ret = PTR_ERR(msm_host->sfpb);
}
}
ret = dsi_host_parse_dt(msm_host); if (ret) return dev_err_probe(&pdev->dev, ret, "%s: failed to parse dt\n",
__func__);
msm_host->ctrl_base = msm_ioremap_size(pdev, "dsi_ctrl", &msm_host->ctrl_size); if (IS_ERR(msm_host->ctrl_base)) return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->ctrl_base), "%s: unable to map Dsi ctrl base\n", __func__);
pm_runtime_enable(&pdev->dev);
msm_host->cfg_hnd = dsi_get_config(msm_host); if (!msm_host->cfg_hnd) return dev_err_probe(&pdev->dev, -EINVAL, "%s: get config failed\n", __func__);
cfg = msm_host->cfg_hnd->cfg;
msm_host->id = dsi_host_get_id(msm_host); if (msm_host->id < 0) return dev_err_probe(&pdev->dev, msm_host->id, "%s: unable to identify DSI host index\n",
__func__);
/* fixup base address by io offset */
msm_host->ctrl_base += cfg->io_offset;
ret = devm_regulator_bulk_get_const(&pdev->dev, cfg->num_regulators,
cfg->regulator_data,
&msm_host->supplies); if (ret) return ret;
ret = dsi_clk_init(msm_host); if (ret) return dev_err_probe(&pdev->dev, ret, "%s: unable to initialize dsi clks\n", __func__);
msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL); if (!msm_host->rx_buf) return -ENOMEM;
ret = devm_pm_opp_set_clkname(&pdev->dev, "byte"); if (ret) return ret; /* OPP table is optional */
ret = devm_pm_opp_of_add_table(&pdev->dev); if (ret && ret != -ENODEV) return dev_err_probe(&pdev->dev, ret, "invalid OPP table in device tree\n");
msm_host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (!msm_host->irq) return dev_err_probe(&pdev->dev, -EINVAL, "failed to get irq\n");
/* do not autoenable, will be enabled later */
ret = devm_request_irq(&pdev->dev, msm_host->irq, dsi_host_irq,
IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, "dsi_isr", msm_host); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "failed to request IRQ%u\n",
msm_host->irq);
/* TODO: make sure dsi_cmd_mdp is idle. * Since DSI6G v1.2.0, we can set DSI_TRIG_CTRL.BLOCK_DMA_WITHIN_FRAME * to ask H/W to wait until cmd mdp is idle. S/W wait is not needed. * How to handle the old versions? Wait for mdp cmd done?
*/
/* * mdss interrupt is generated in mdp core clock domain * mdp clock need to be enabled to receive dsi interrupt
*/
pm_runtime_get_sync(&msm_host->pdev->dev);
cfg_hnd->ops->link_clk_set_rate(msm_host);
cfg_hnd->ops->link_clk_enable(msm_host);
/* TODO: vote for bus bandwidth */
if (!(msg->flags & MIPI_DSI_MSG_USE_LPM))
dsi_set_tx_power_mode(0, msm_host);
ret = dsi_cmds2buf_tx(msm_host, &max_pkt_size_msg); if (ret < 2) {
pr_err("%s: Set max pkt size failed, %d\n",
__func__, ret); return -EINVAL;
}
if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) &&
(cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_1)) { /* Clear the RDBK_DATA registers */
dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL,
DSI_RDBK_DATA_CTRL_CLR);
wmb(); /* make sure the RDBK registers are cleared */
dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL, 0);
wmb(); /* release cleared status before transfer */
}
/* * once cmd_dma_done interrupt received, * return data from client is ready and stored * at RDBK_DATA register already * since rx fifo is 16 bytes, dcs header is kept at first loop, * after that dcs header lost during shift into registers
*/
dlen = dsi_cmd_dma_rx(msm_host, buf, rx_byte, pkt_size);
if (!end) {
dlen -= 2; /* 2 crc */
dlen -= diff;
buf += dlen; /* next start position */
data_byte = 14; /* NOT first read */ if (rlen < data_byte)
pkt_size += rlen; else
pkt_size += data_byte;
DBG("buf=%p dlen=%d diff=%d", buf, dlen, diff);
}
}
/* * For single Long read, if the requested rlen < 10, * we need to shift the start position of rx * data buffer to skip the bytes which are not * updated.
*/ if (pkt_size < 10 && !short_response)
buf = msm_host->rx_buf + (10 - rlen); else
buf = msm_host->rx_buf;
cmd = buf[0]; switch (cmd) { case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
pr_err("%s: rx ACK_ERR_PACLAGE\n", __func__);
ret = 0; break; case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
ret = dsi_short_read1_resp(buf, msg); break; case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
ret = dsi_short_read2_resp(buf, msg); break; case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
ret = dsi_long_read_resp(buf, msg); break; default:
pr_warn("%s:Invalid response cmd\n", __func__);
ret = 0;
}
ret = cfg_hnd->ops->calc_clk_rate(msm_host, is_bonded_dsi); if (ret) {
pr_err("%s: unable to calc clk rate, %d\n", __func__, ret); return;
}
/* CPHY transmits 16 bits over 7 clock cycles * "byte_clk" is in units of 16-bits (see dsi_calc_pclk), * so multiply by 7 to get the "bitclk rate"
*/ if (msm_host->cphy_mode)
clk_req->bitclk_rate = msm_host->byte_clk_rate * 7; else
clk_req->bitclk_rate = msm_host->byte_clk_rate * 8;
clk_req->escclk_rate = msm_host->esc_clk_rate;
}
/* TODO: clock should be turned off for command mode, * and only turned on before MDP START. * This part of code should be enabled once mdp driver support it.
*/ /* if (msm_panel->mode == MSM_DSI_CMD_MODE) { * dsi_link_clk_disable(msm_host); * pm_runtime_put(&msm_host->pdev->dev); * }
*/
msm_host->enabled = true; return 0;
}
int msm_dsi_host_disable(struct mipi_dsi_host *host)
{ struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
/* Since we have disabled INTF, the video engine won't stop so that * the cmd engine will be blocked. * Reset to disable video engine so that we can send off cmd.
*/
dsi_sw_reset(msm_host);
msm_host->byte_intf_clk_rate = msm_host->byte_clk_rate; if (phy_shared_timings->byte_intf_clk_div_2)
msm_host->byte_intf_clk_rate /= 2;
msm_dsi_sfpb_config(msm_host, true);
ret = regulator_bulk_enable(msm_host->cfg_hnd->cfg->num_regulators,
msm_host->supplies); if (ret) {
pr_err("%s:Failed to enable vregs.ret=%d\n",
__func__, ret); goto unlock_ret;
}
pm_runtime_get_sync(&msm_host->pdev->dev);
ret = cfg_hnd->ops->link_clk_set_rate(msm_host); if (!ret)
ret = cfg_hnd->ops->link_clk_enable(msm_host); if (ret) {
pr_err("%s: failed to enable link clocks. ret=%d\n",
__func__, 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.