diff --git a/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts index 1fa49d8a060d..74edd4e9ac0f 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017,2019 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -38,6 +38,12 @@ qcom,display-type = "primary"; }; +&mdss_fb2 { + qcom,cont-splash-memory { + linux,contiguous-region = <&cont_splash_mem>; + }; +}; + &slim_aud { tasha_codec { wsa_spkr_sd1: msm_cdc_pinctrll { diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index df37dffd1286..7f4da691ac7c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -841,6 +841,9 @@ static void msm_isp_sync_dual_cam_frame_id( ms_res->src_info[i]->dual_hw_ms_info.index); } } + /* the number of frames that are dropped */ + vfe_dev->isp_page->dual_cam_drop = + frame_id - (src_info->frame_id + 1); ms_res->active_src_mask |= (1 << src_info->dual_hw_ms_info.index); src_info->frame_id = frame_id; src_info->dual_hw_ms_info.sync_state = MSM_ISP_DUAL_CAM_SYNC; @@ -878,6 +881,8 @@ void msm_isp_increment_frame_id(struct vfe_device *vfe_dev, src_info->dual_hw_ms_info.index)) { pr_err_ratelimited("Frame out of sync on vfe %d\n", vfe_dev->pdev->id); + /* Notify to do reconfig at SW sync drop*/ + vfe_dev->isp_page->dual_cam_drop_detected = 1; /* * set this isp as async mode to force *it sync again at the next sof @@ -2153,8 +2158,8 @@ static int msm_isp_process_done_buf(struct vfe_device *vfe_dev, MSM_ISP_BUFFER_STATE_PUT_BUF; buf->buf_debug.put_state_last ^= 1; rc = vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr, - buf->bufq_handle, buf->buf_idx, time_stamp, - frame_id, stream_info->runtime_output_format); + buf->bufq_handle, buf->buf_idx, time_stamp, + frame_id, stream_info->runtime_output_format); if (rc == -EFAULT) { msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR); diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index cc11ede6a456..ea81ccda4ee5 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2085,7 +2085,8 @@ static void hdcp_lib_clean(struct hdcp_lib_handle *handle) handle->authenticated = false; /* AV mute the sink first to avoid artifacts */ - handle->client_ops->mute_sink(handle->client_ctx); + if (handle->client_ops->mute_sink) + handle->client_ops->mute_sink(handle->client_ctx); hdcp_lib_txmtr_deinit(handle); if (!handle->legacy_app) @@ -2332,19 +2333,20 @@ bool hdcp1_check_if_supported_load_app(void) /* start hdcp1 app */ if (hdcp1_supported && !hdcp1_handle->qsee_handle) { + mutex_init(&hdcp1_ta_cmd_lock); rc = qseecom_start_app(&hdcp1_handle->qsee_handle, HDCP1_APP_NAME, QSEECOM_SBUFF_SIZE); if (rc) { pr_err("hdcp1 qseecom_start_app failed %d\n", rc); hdcp1_supported = false; + hdcp1_srm_supported = false; kfree(hdcp1_handle); } } /* if hdcp1 app succeeds load SRM TA as well */ if (hdcp1_supported && !hdcp1_handle->srm_handle) { - mutex_init(&hdcp1_ta_cmd_lock); rc = qseecom_start_app(&hdcp1_handle->srm_handle, SRMAPP_NAME, QSEECOM_SBUFF_SIZE); @@ -2395,13 +2397,19 @@ int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb) if (aksv_msb == NULL || aksv_lsb == NULL) return -EINVAL; - if (!hdcp1_supported || !hdcp1_handle) - return -EINVAL; + mutex_lock(&hdcp1_ta_cmd_lock); + + if (!hdcp1_supported || !hdcp1_handle) { + rc = -EINVAL; + goto end; + } hdcp1_qsee_handle = hdcp1_handle->qsee_handle; - if (!hdcp1_qsee_handle) - return -EINVAL; + if (!hdcp1_qsee_handle) { + rc = -EINVAL; + goto end; + } /* set keys and request aksv */ key_set_req = (struct hdcp1_key_set_req *)hdcp1_qsee_handle->sbuf; @@ -2417,13 +2425,15 @@ int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb) if (rc < 0) { pr_err("qseecom cmd failed err=%d\n", rc); - return -ENOKEY; + rc = -ENOKEY; + goto end; } rc = key_set_rsp->ret; if (rc) { pr_err("set key cmd failed, rsp=%d\n", key_set_rsp->ret); - return -ENOKEY; + rc = -ENOKEY; + goto end; } /* copy bytes into msb and lsb */ @@ -2436,7 +2446,9 @@ int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb) *aksv_lsb |= key_set_rsp->ksv[6] << 8; *aksv_lsb |= key_set_rsp->ksv[7]; - return 0; +end: + mutex_unlock(&hdcp1_ta_cmd_lock); + return rc; } int hdcp1_validate_receiver_ids(struct hdcp_srm_device_id_t *device_ids, @@ -2576,8 +2588,10 @@ int hdcp1_set_enc(bool enable) hdcp1_qsee_handle = hdcp1_handle->qsee_handle; - if (!hdcp1_qsee_handle) - return -EINVAL; + if (!hdcp1_qsee_handle) { + rc = -EINVAL; + goto end; + } if (hdcp1_enc_enabled == enable) { pr_info("already %s\n", enable ? "enabled" : "disabled"); diff --git a/drivers/video/fbdev/msm/mdss_cec_core.c b/drivers/video/fbdev/msm/mdss_cec_core.c index 1d9950494d65..ab7a8056016a 100644 --- a/drivers/video/fbdev/msm/mdss_cec_core.c +++ b/drivers/video/fbdev/msm/mdss_cec_core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -594,6 +594,41 @@ end: return ret; } +static ssize_t cec_wta_clear_logical_addr(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int clear_flag; + unsigned long flags; + ssize_t ret; + struct cec_ctl *ctl = cec_get_ctl(dev); + struct cec_ops *ops; + + if (!ctl) { + pr_err("Invalid ctl\n"); + ret = -EINVAL; + goto end; + } + + ops = ctl->init_data.ops; + + ret = kstrtoint(buf, 10, &clear_flag); + if (ret) { + pr_err("kstrtoint failed\n"); + goto end; + } + + ret = count; + + spin_lock_irqsave(&ctl->lock, flags); + if (ctl->enabled) { + if (ops && ops->clear_logical_addr) + ops->clear_logical_addr(ops->data, !!clear_flag); + } + spin_unlock_irqrestore(&ctl->lock, flags); +end: + return ret; +} + static ssize_t cec_rda_msg(struct device *dev, struct device_attribute *attr, char *buf) { @@ -703,6 +738,8 @@ static DEVICE_ATTR(enable_compliance, S_IRUGO | S_IWUSR, cec_rda_enable_compliance, cec_wta_enable_compliance); static DEVICE_ATTR(logical_addr, S_IRUSR | S_IWUSR, cec_rda_logical_addr, cec_wta_logical_addr); +static DEVICE_ATTR(clear_logical_addr, 0200, + NULL, cec_wta_clear_logical_addr); static DEVICE_ATTR(rd_msg, S_IRUGO, cec_rda_msg, NULL); static DEVICE_ATTR(wr_msg, S_IWUSR | S_IRUSR, NULL, cec_wta_msg); @@ -710,6 +747,7 @@ static struct attribute *cec_fs_attrs[] = { &dev_attr_enable.attr, &dev_attr_enable_compliance.attr, &dev_attr_logical_addr.attr, + &dev_attr_clear_logical_addr.attr, &dev_attr_rd_msg.attr, &dev_attr_wr_msg.attr, NULL, diff --git a/drivers/video/fbdev/msm/mdss_cec_core.h b/drivers/video/fbdev/msm/mdss_cec_core.h index 12b7677c5dee..481884a732d8 100644 --- a/drivers/video/fbdev/msm/mdss_cec_core.h +++ b/drivers/video/fbdev/msm/mdss_cec_core.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2016, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -67,6 +67,7 @@ struct cec_ops { int (*send_msg)(void *data, struct cec_msg *msg); void (*wt_logical_addr)(void *data, u8 addr); + void (*clear_logical_addr)(void *data, bool flag); void (*wakeup_en)(void *data, bool en); bool (*is_wakeup_en)(void *data); void (*device_suspend)(void *data, bool suspend); diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index d89afbc93749..cc6278b01a1c 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1,7 +1,7 @@ /* * Core MDSS framebuffer driver. * - * Copyright (c) 2008-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2008-2020, The Linux Foundation. All rights reserved. * Copyright (C) 2007 Google Incorporated * * This software is licensed under the terms of the GNU General Public @@ -3760,16 +3760,19 @@ static int mdss_fb_pan_display(struct fb_var_screeninfo *var, { struct mdp_display_commit disp_commit; struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct mdss_data_type *mdata = mfd_to_mdata(mfd); /* - * during mode switch through mode sysfs node, it will trigger a - * pan_display after switch. This assumes that fb has been adjusted, - * however when using overlays we may not have the right size at this - * point, so it needs to go through PREPARE first. Abort pan_display - * operations until that happens + * Abort pan_display operations in following cases: + * 1. during mode switch through mode sysfs node, it will trigger a + * pan_display after switch. This assumes that fb has been adjusted, + * however when using overlays we may not have the right size at this + * point, so it needs to go through PREPARE first. + * 2. When the splash handoff is pending. */ - if (mfd->switch_state != MDSS_MDP_NO_UPDATE_REQUESTED) { - pr_debug("fb%d: pan_display skipped during switch\n", + if ((mfd->switch_state != MDSS_MDP_NO_UPDATE_REQUESTED) || + (mdss_fb_is_hdmi_primary(mfd) && mdata->handoff_pending)) { + pr_debug("fb%d: pan_display skipped during switch or handoff\n", mfd->index); return 0; } @@ -4325,17 +4328,23 @@ static int mdss_fb_set_par(struct fb_info *info) mfd->fbi->fix.smem_len = PAGE_ALIGN(mfd->fbi->fix.line_length * mfd->fbi->var.yres) * mfd->fb_page; - old_format = mdss_grayscale_to_mdp_format(var->grayscale); - if (!IS_ERR_VALUE(old_format)) { + old_format = mfd->panel_info->out_format; + mfd->panel_info->out_format = + mdss_grayscale_to_mdp_format(var->grayscale); + if (!IS_ERR_VALUE(mfd->panel_info->out_format)) { if (old_format != mfd->panel_info->out_format) mfd->panel_reconfig = true; } + if (mdss_fb_is_hdmi_primary(mfd) && mfd->panel_reconfig) + mfd->force_null_commit = true; + if (mfd->panel_reconfig || (mfd->fb_imgType != old_imgType)) { mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); mdss_fb_var_to_panelinfo(var, mfd->panel_info); mdss_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable); mfd->panel_reconfig = false; + mfd->force_null_commit = false; } return ret; diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 301c1386a639..4d2888246b7c 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -292,6 +292,7 @@ struct msm_fb_data_type { int op_enable; u32 fb_imgType; int panel_reconfig; + int force_null_commit; u32 panel_orientation; u32 dst_format; diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c index 8df9080129b9..0b394ea270cd 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c +++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1348,16 +1348,16 @@ static int hdcp_1x_authentication_part2(struct hdcp_1x *hdcp) } do { - rc = hdcp_1x_transfer_v_h(hdcp); - if (rc) - goto error; - /* do not proceed further if no device connected */ if (!hdcp->current_tp.dev_count) { rc = -EINVAL; goto error; } + rc = hdcp_1x_transfer_v_h(hdcp); + if (rc) + goto error; + rc = hdcp_1x_write_ksv_fifo(hdcp); } while (--v_retry && rc); error: diff --git a/drivers/video/fbdev/msm/mdss_hdmi_cec.c b/drivers/video/fbdev/msm/mdss_hdmi_cec.c index a4ed01210e04..3eda29edff4d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_cec.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_cec.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,7 @@ #include #include #include +#include #include "mdss_hdmi_cec.h" #include "mdss_panel.h" @@ -33,12 +34,19 @@ #define CEC_OP_KEY_PRESS 0x44 #define CEC_OP_STANDBY 0x36 +#define CEC_RECV_Q_SIZE 4 +#define CEC_RECV_Q_MASK 3 + struct hdmi_cec_ctrl { bool cec_enabled; bool cec_wakeup_en; bool cec_device_suspend; - + bool cec_clear_logical_addr; + u32 cec_logical_addr; u32 cec_msg_wr_status; + struct cec_msg recv_msg[CEC_RECV_Q_SIZE]; + u32 head; + u32 tail; spinlock_t lock; struct work_struct cec_read_work; struct completion cec_msg_wr_done; @@ -60,6 +68,20 @@ static int hdmi_cec_msg_send(void *data, struct cec_msg *msg) return -EINVAL; } + if (msg->sender_id != cec_ctrl->cec_logical_addr && + msg->recvr_id == 0xF) { + /* + * If the current logical address is not the + * same as the sender_id and if the message is + * broadcasting, the message is looping back. + * Abort the message sending in that case + */ + DEV_ERR("%s: abort potential MAL msg %d:%d logical %d\n", + __func__, msg->sender_id, msg->recvr_id, + cec_ctrl->cec_logical_addr); + return -EINVAL; + } + io = cec_ctrl->init_data.io; reinit_completion(&cec_ctrl->cec_msg_wr_done); @@ -164,14 +186,87 @@ static void hdmi_cec_deinit_input_event(struct hdmi_cec_ctrl *cec_ctrl) cec_ctrl->input = NULL; } +static int hdmi_cec_msg_read(struct hdmi_cec_ctrl *cec_ctrl) +{ + struct dss_io_data *io = NULL; + struct cec_msg *msg; + u32 data; + int i; + u32 head; + u32 tail; + + if (!cec_ctrl || !cec_ctrl->init_data.io) { + DEV_ERR("%s: invalid input\n", __func__); + return -EINVAL; + } + + if (!cec_ctrl->cec_enabled) { + DEV_ERR("%s: cec not enabled\n", __func__); + return -ENODEV; + } + + head = cec_ctrl->head; + tail = READ_ONCE(cec_ctrl->tail); + if (CIRC_SPACE(head, tail, CEC_RECV_Q_SIZE) < 1) { + DEV_ERR("%s: no more space to hold the buffer\n", __func__); + return 0; /* Let's just kick the thread */ + } + + msg = &cec_ctrl->recv_msg[head]; + + io = cec_ctrl->init_data.io; + data = DSS_REG_R(io, HDMI_CEC_RD_DATA); + + msg->recvr_id = (data & 0x000F); + msg->sender_id = (data & 0x00F0) >> 4; + msg->frame_size = (data & 0x1F00) >> 8; + if (msg->frame_size < 1 || msg->frame_size > MAX_CEC_FRAME_SIZE) { + DEV_ERR("%s: invalid message (frame length = %d)\n", + __func__, msg->frame_size); + return -EINVAL; + } else if (msg->frame_size == 1) { + DEV_DBG("%s: polling message (dest[%x] <- init[%x])\n", + __func__, msg->recvr_id, msg->sender_id); + return -EINVAL; + } + + /* data block 0 : opcode */ + data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA); + msg->opcode = data & 0xFF; + + /* data block 1-14 : operand 0-13 */ + for (i = 0; i < msg->frame_size - 2; i++) { + data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA); + msg->operand[i] = data & 0xFF; + } + + for (; i < MAX_OPERAND_SIZE; i++) + msg->operand[i] = 0; + + /* + * Clearing the logical address is used when the system doesn't + * need to process CEC command any more. + */ + if (cec_ctrl->cec_clear_logical_addr) + return -EINVAL; + + /* Update head */ + smp_store_release(&cec_ctrl->head, (head + 1) & CEC_RECV_Q_MASK); + + DEV_DBG("%s: opcode 0x%x, wakup_en %d, device_suspend %d\n", __func__, + msg->opcode, cec_ctrl->cec_wakeup_en, + cec_ctrl->cec_device_suspend); + + return 0; +} + static void hdmi_cec_msg_recv(struct work_struct *work) { - int i; - u32 data; struct hdmi_cec_ctrl *cec_ctrl = NULL; - struct dss_io_data *io = NULL; struct cec_msg msg; struct cec_cbs *cbs; + u32 head; + u32 tail; cec_ctrl = container_of(work, struct hdmi_cec_ctrl, cec_read_work); if (!cec_ctrl || !cec_ctrl->init_data.io) { @@ -179,73 +274,42 @@ static void hdmi_cec_msg_recv(struct work_struct *work) return; } - if (!cec_ctrl->cec_enabled) { - DEV_ERR("%s: cec not enabled\n", __func__); - return; - } - - io = cec_ctrl->init_data.io; cbs = cec_ctrl->init_data.cbs; - data = DSS_REG_R(io, HDMI_CEC_RD_DATA); + /* Read head before reading contents */ + head = smp_load_acquire(&cec_ctrl->head); + tail = cec_ctrl->tail; + while (CIRC_CNT(head, tail, CEC_RECV_Q_SIZE) >= 1) { + memcpy(&msg, &cec_ctrl->recv_msg[tail], sizeof(msg)); + tail = (tail + 1) & CEC_RECV_Q_MASK; - msg.recvr_id = (data & 0x000F); - msg.sender_id = (data & 0x00F0) >> 4; - msg.frame_size = (data & 0x1F00) >> 8; - DEV_DBG("%s: Recvd init=[%u] dest=[%u] size=[%u]\n", __func__, - msg.sender_id, msg.recvr_id, - msg.frame_size); + /* Finishing reading before incrementing tail */ + smp_store_release(&cec_ctrl->tail, tail); - if (msg.frame_size < 1 || msg.frame_size > MAX_CEC_FRAME_SIZE) { - DEV_ERR("%s: invalid message (frame length = %d)\n", - __func__, msg.frame_size); - return; - } else if (msg.frame_size == 1) { - DEV_DBG("%s: polling message (dest[%x] <- init[%x])\n", - __func__, msg.recvr_id, msg.sender_id); - return; + if ((msg.opcode == CEC_OP_SET_STREAM_PATH || + msg.opcode == CEC_OP_KEY_PRESS) && + cec_ctrl->input && cec_ctrl->cec_wakeup_en && + cec_ctrl->cec_device_suspend) { + DEV_DBG("%s: Sending power on at wakeup\n", __func__); + input_report_key(cec_ctrl->input, KEY_POWER, 1); + input_sync(cec_ctrl->input); + input_report_key(cec_ctrl->input, KEY_POWER, 0); + input_sync(cec_ctrl->input); + } + + if ((msg.opcode == CEC_OP_STANDBY) && + cec_ctrl->input && cec_ctrl->cec_wakeup_en && + !cec_ctrl->cec_device_suspend) { + DEV_DBG("%s: Sending power off on standby\n", __func__); + input_report_key(cec_ctrl->input, KEY_POWER, 1); + input_sync(cec_ctrl->input); + input_report_key(cec_ctrl->input, KEY_POWER, 0); + input_sync(cec_ctrl->input); + } + + if (cbs && cbs->msg_recv_notify) + cbs->msg_recv_notify(cbs->data, &msg); } - - /* data block 0 : opcode */ - data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA); - msg.opcode = data & 0xFF; - - /* data block 1-14 : operand 0-13 */ - for (i = 0; i < msg.frame_size - 2; i++) { - data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA); - msg.operand[i] = data & 0xFF; - } - - for (; i < MAX_OPERAND_SIZE; i++) - msg.operand[i] = 0; - - DEV_DBG("%s: opcode 0x%x, wakup_en %d, device_suspend %d\n", __func__, - msg.opcode, cec_ctrl->cec_wakeup_en, - cec_ctrl->cec_device_suspend); - - if ((msg.opcode == CEC_OP_SET_STREAM_PATH || - msg.opcode == CEC_OP_KEY_PRESS) && - cec_ctrl->input && cec_ctrl->cec_wakeup_en && - cec_ctrl->cec_device_suspend) { - DEV_DBG("%s: Sending power on at wakeup\n", __func__); - input_report_key(cec_ctrl->input, KEY_POWER, 1); - input_sync(cec_ctrl->input); - input_report_key(cec_ctrl->input, KEY_POWER, 0); - input_sync(cec_ctrl->input); - } - - if ((msg.opcode == CEC_OP_STANDBY) && - cec_ctrl->input && cec_ctrl->cec_wakeup_en && - !cec_ctrl->cec_device_suspend) { - DEV_DBG("%s: Sending power off on standby\n", __func__); - input_report_key(cec_ctrl->input, KEY_POWER, 1); - input_sync(cec_ctrl->input); - input_report_key(cec_ctrl->input, KEY_POWER, 0); - input_sync(cec_ctrl->input); - } - - if (cbs && cbs->msg_recv_notify) - cbs->msg_recv_notify(cbs->data, &msg); } /** @@ -308,8 +372,12 @@ int hdmi_cec_isr(void *input) if ((cec_intr & BIT(6)) && (cec_intr & BIT(7))) { DEV_DBG("%s: CEC_IRQ_FRAME_RD_DONE\n", __func__); + rc = hdmi_cec_msg_read(cec_ctrl); + if (!rc) + queue_work(cec_ctrl->init_data.workq, + &cec_ctrl->cec_read_work); + DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(6)); - queue_work(cec_ctrl->init_data.workq, &cec_ctrl->cec_read_work); } return rc; @@ -360,8 +428,23 @@ static void hdmi_cec_write_logical_addr(void *input, u8 addr) return; } - if (cec_ctrl->cec_enabled) + if (cec_ctrl->cec_enabled) { DSS_REG_W(cec_ctrl->init_data.io, HDMI_CEC_ADDR, addr & 0xF); + cec_ctrl->cec_logical_addr = addr & 0xF; + } +} + +static void hdmi_cec_clear_logical_addr(void *input, bool clear_flag) +{ + struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input; + + if (!cec_ctrl || !cec_ctrl->init_data.io) { + DEV_ERR("%s: Invalid input\n", __func__); + return; + } + + if (cec_ctrl->cec_enabled) + cec_ctrl->cec_clear_logical_addr = clear_flag; } static int hdmi_cec_enable(void *input, bool enable) @@ -474,6 +557,7 @@ void *hdmi_cec_init(struct hdmi_cec_init_data *init_data) /* populate hardware specific operations to client */ ops->send_msg = hdmi_cec_msg_send; ops->wt_logical_addr = hdmi_cec_write_logical_addr; + ops->clear_logical_addr = hdmi_cec_clear_logical_addr; ops->enable = hdmi_cec_enable; ops->data = cec_ctrl; ops->wakeup_en = hdmi_cec_wakeup_en; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 0c04fd35c0d5..cb4ef1781dfb 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -51,6 +51,9 @@ /* Support for first 5 EDID blocks */ #define MAX_EDID_SIZE (EDID_BLOCK_SIZE * MAX_EDID_BLOCKS) +/* Max refresh rate supported in Hz */ +#define MAX_REFRESH_RATE_SUPPORTED 60 + #define BUFF_SIZE_3D 128 #define DTD_MAX 0x04 @@ -89,6 +92,7 @@ enum extended_data_block_types { VIDEO_CAPABILITY_DATA_BLOCK = 0x0, VENDOR_SPECIFIC_VIDEO_DATA_BLOCK = 0x01, HDMI_VIDEO_DATA_BLOCK = 0x04, + COLORIMETRY_DATA_BLOCK = 0x05, HDR_STATIC_METADATA_DATA_BLOCK = 0x06, Y420_VIDEO_DATA_BLOCK = 0x0E, VIDEO_FORMAT_PREFERENCE_DATA_BLOCK = 0x0D, @@ -122,6 +126,16 @@ struct hdmi_edid_sink_caps { bool ind_view_support; }; +struct hdmi_edid_y420_cmdb { + u8 *vic_list; + u32 len; +}; + +struct hdmi_edid_colorimetry { + u8 standards; + u8 metadata_profiles; +}; + struct hdmi_edid_ctrl { u8 pt_scan_info; u8 it_scan_info; @@ -147,12 +161,18 @@ struct hdmi_edid_ctrl { bool keep_resv_timings; bool edid_override; bool hdr_supported; + bool override_default_vic; + + bool y420_cmdb_present; + bool y420_cmdb_supports_all; + struct hdmi_edid_y420_cmdb y420_cmdb; struct hdmi_edid_sink_data sink_data; struct hdmi_edid_init_data init_data; struct hdmi_edid_sink_caps sink_caps; struct hdmi_edid_override_data override_data; struct hdmi_edid_hdr_data hdr_data; + struct hdmi_edid_colorimetry colorimetry; }; static bool hdmi_edid_is_mode_supported(struct hdmi_edid_ctrl *edid_ctrl, @@ -187,6 +207,9 @@ static int hdmi_edid_reset_parser(struct hdmi_edid_ctrl *edid_ctrl) edid_ctrl->sink_data.num_of_elements = 0; + /* reset deep color */ + edid_ctrl->deep_color = 0; + /* reset scan info data */ edid_ctrl->pt_scan_info = 0; edid_ctrl->it_scan_info = 0; @@ -200,6 +223,7 @@ static int hdmi_edid_reset_parser(struct hdmi_edid_ctrl *edid_ctrl) /* reset resolution related sink data */ memset(&edid_ctrl->sink_data, 0, sizeof(edid_ctrl->sink_data)); + memset(&edid_ctrl->sink_caps, 0, sizeof(edid_ctrl->sink_caps)); /* reset audio related data */ memset(edid_ctrl->audio_data_block, 0, @@ -224,6 +248,10 @@ static int hdmi_edid_reset_parser(struct hdmi_edid_ctrl *edid_ctrl) edid_ctrl->hdr_data.avg_luminance = 0; edid_ctrl->hdr_data.min_luminance = 0; + edid_ctrl->y420_cmdb_present = false; + edid_ctrl->y420_cmdb_supports_all = false; + kfree(edid_ctrl->y420_cmdb.vic_list); + memset(&edid_ctrl->y420_cmdb, 0, sizeof(edid_ctrl->y420_cmdb)); return 0; } @@ -915,28 +943,6 @@ static const u8 *hdmi_edid_find_hfvsdb(const u8 *in_buf) return vsd; } -static void hdmi_edid_set_y420_support(struct hdmi_edid_ctrl *edid_ctrl, - u32 video_format) -{ - u32 i = 0; - - if (!edid_ctrl) { - DEV_ERR("%s: Invalid input\n", __func__); - return; - } - - for (i = 0; i < edid_ctrl->sink_data.num_of_elements; ++i) { - if (video_format == - edid_ctrl->sink_data.disp_mode_list[i].video_format) { - edid_ctrl->sink_data.disp_mode_list[i].y420_support = - true; - DEV_DBG("%s: Yuv420 supported for format %d\n", - __func__, - edid_ctrl->sink_data.disp_mode_list[i].video_format); - } - } -} - static void hdmi_edid_add_sink_y420_format(struct hdmi_edid_ctrl *edid_ctrl, u32 video_format) { @@ -963,6 +969,17 @@ static void hdmi_edid_add_sink_y420_format(struct hdmi_edid_ctrl *edid_ctrl, video_format, msm_hdmi_mode_2string(video_format), supported ? "Supported" : "Not-Supported"); + /* override the default resolution */ + if (edid_ctrl->override_default_vic) { + if (!ret && supported) { + sink->disp_mode_list[0].video_format = video_format; + sink->disp_mode_list[0].y420_support = true; + sink->disp_mode_list[0].rgb_support = false; + edid_ctrl->override_default_vic = false; + return; + } + } + if (!ret && supported) { sink->disp_mode_list[sink->num_of_elements].video_format = video_format; @@ -1039,10 +1056,12 @@ static void hdmi_edid_parse_Y420CMDB(struct hdmi_edid_ctrl *edid_ctrl, { u32 offset = 0; u8 svd_len = 0; - u32 i = 0, j = 0; + u32 i = 0, j = 0, k = 0; u32 video_format = 0; u32 len = 0; const u8 *svd = NULL; + struct hdmi_edid_y420_cmdb *y420_cmdb = NULL; + if (!edid_ctrl) { DEV_ERR("%s: invalid input\n", __func__); @@ -1052,6 +1071,18 @@ static void hdmi_edid_parse_Y420CMDB(struct hdmi_edid_ctrl *edid_ctrl, offset += 2; len = in_buf[0] & 0x1F; + /* + * When the Length field is set to L==1, the Y420CMDB does not include + * a YCBCR 4:2:0 Capability Bit Map and all the SVDs in the regular + * Video Data Block support YCBCR 4:2:0 sampling mode. + */ + if (len == 1) { + DEV_DBG("%s: All SVDs supports Y420 sampling mode, len = %d\n", + __func__, len); + edid_ctrl->y420_cmdb_supports_all = true; + return; + } + /* * The Y420 Capability map data block should be parsed along with the * video data block. Each bit in Y420CMDB maps to each SVD in data @@ -1060,16 +1091,33 @@ static void hdmi_edid_parse_Y420CMDB(struct hdmi_edid_ctrl *edid_ctrl, svd = hdmi_edid_find_block(edid_ctrl->edid_buf+0x80, DBC_START_OFFSET, VIDEO_DATA_BLOCK, &svd_len); - ++svd; - for (i = 0; i < svd_len; i++, j++) { - video_format = *svd & 0x7F; - if (in_buf[offset] & (1 << j)) - hdmi_edid_set_y420_support(edid_ctrl, video_format); + if (!svd_len) + return; - if (j & 0x80) { - j = j/8; + y420_cmdb = &edid_ctrl->y420_cmdb; + y420_cmdb->vic_list = kzalloc(svd_len, GFP_KERNEL); + if (!y420_cmdb->vic_list) { + DEV_ERR("%s: failed to allocated memory for y420 vic_list\n", + __func__); + return; + } + + ++svd; + + for (i = 0, k = 0; i < svd_len; i++, svd++) { + video_format = *svd & 0x7F; + if (in_buf[offset] & (1 << j)) { + y420_cmdb->vic_list[k++] = video_format; + DEV_DBG("%s: Y420 capability for VIC %d\n", + __func__, video_format); + y420_cmdb->len++; + } + + j++; + if (j & 0x8) { + j = 0; offset++; - if (offset >= len) + if (offset >= len + 1) break; } } @@ -1104,6 +1152,26 @@ static void hdmi_edid_parse_hvdb(struct hdmi_edid_ctrl *edid_ctrl, } +static void hdmi_edid_parse_colorimetry( + struct hdmi_edid_ctrl *edid_ctrl, const u8 *in_buf) +{ + u8 len = 0; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + + len = in_buf[0] & 0x1F; + if ((in_buf[1] != COLORIMETRY_DATA_BLOCK) || (len < 3)) { + DEV_ERR("%s: Not a Colorimetry tag code\n", __func__); + return; + } + + edid_ctrl->colorimetry.standards = in_buf[2]; + edid_ctrl->colorimetry.metadata_profiles = in_buf[3]; +} + static void hdmi_edid_extract_extended_data_blocks( struct hdmi_edid_ctrl *edid_ctrl, const u8 *in_buf) { @@ -1130,8 +1198,8 @@ static void hdmi_edid_extract_extended_data_blocks( break; } - /* The extended data block should at least be 2 bytes long */ - if (len < 2) { + /* The extended data block should at least be 1 bytes long */ + if (len < 1) { DEV_DBG("%s: invalid block size\n", __func__); continue; } @@ -1173,6 +1241,7 @@ static void hdmi_edid_extract_extended_data_blocks( case Y420_CAPABILITY_MAP_DATA_BLOCK: DEV_DBG("%s found Y420CMDB byte 3 = 0x%x", __func__, etag[2]); + edid_ctrl->y420_cmdb_present = true; hdmi_edid_parse_Y420CMDB(edid_ctrl, etag); break; case Y420_VIDEO_DATA_BLOCK: @@ -1186,6 +1255,11 @@ static void hdmi_edid_extract_extended_data_blocks( hdmi_edid_parse_hdrdb(edid_ctrl, etag); edid_ctrl->hdr_supported = true; break; + case COLORIMETRY_DATA_BLOCK: + DEV_DBG("%s found COLORIMETRY block. byte 3 = 0x%x", + __func__, etag[2]); + hdmi_edid_parse_colorimetry(edid_ctrl, etag); + break; default: DEV_DBG("%s: Tag Code %d not supported\n", __func__, etag[1]); @@ -1216,6 +1290,9 @@ static void hdmi_edid_extract_3d_present(struct hdmi_edid_ctrl *edid_ctrl, } offset = HDMI_VSDB_3D_EVF_DATA_OFFSET(vsd); + if (offset >= len - 1) + return; + DEV_DBG("%s: EDID: 3D present @ 0x%x = %02x\n", __func__, offset, vsd[offset]); @@ -1331,7 +1408,10 @@ static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl, return; /* Max TMDS clock is in multiples of 5Mhz. */ - edid_ctrl->sink_caps.max_pclk_in_hz = vsd[7] * 5000000; + if (len >= 7 && vsd[7]) { + edid_ctrl->sink_caps.max_pclk_in_hz = vsd[7] * 5000000; + DEV_DBG("%s: MaxTMDS=%dMHz\n", __func__, (u32)vsd[7] * 5); + } vsd = hdmi_edid_find_hfvsdb(in_buf); @@ -1344,9 +1424,13 @@ static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl, * the sink shall set this filed to 0. The max TMDS support * clock Rate = Max_TMDS_Character_Rates * 5Mhz. */ - if (vsd[5] != 0) + if (vsd[5] != 0) { edid_ctrl->sink_caps.max_pclk_in_hz = vsd[5] * 5000000; + DEV_DBG("%s: HF-VSDB: MaxTMDS=%dMHz\n", + __func__, (u32)vsd[5] * 5); + } + edid_ctrl->sink_caps.scdc_present = (vsd[6] & 0x80) ? true : false; edid_ctrl->sink_caps.scramble_support = @@ -1376,8 +1460,19 @@ static void hdmi_edid_extract_latency_fields(struct hdmi_edid_ctrl *edid_ctrl, vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, VENDOR_SPECIFIC_DATA_BLOCK, &len); - if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE || - !(vsd[8] & BIT(7))) { + if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) { + DEV_DBG("%s: No/Invalid vendor Specific Data Block\n", + __func__); + return; + } + + if (len < 8) { + DEV_DBG("%s: No extra Vendor Specific information present\n", + __func__); + return; + } + + if (!(vsd[8] & BIT(7))) { edid_ctrl->video_latency = (u16)-1; edid_ctrl->audio_latency = (u16)-1; DEV_DBG("%s: EDID: No audio/video latency present\n", __func__); @@ -1410,8 +1505,8 @@ static u32 hdmi_edid_extract_ieee_reg_id(struct hdmi_edid_ctrl *edid_ctrl, return 0; } - DEV_DBG("%s: EDID: VSD PhyAddr=%04x, MaxTMDS=%dMHz\n", __func__, - ((u32)vsd[4] << 8) + (u32)vsd[5], (u32)vsd[7] * 5); + DEV_DBG("%s: EDID: VSD PhyAddr=%04x\n", __func__, + ((u32)vsd[4] << 8) + (u32)vsd[5]); edid_ctrl->physical_address = ((u16)vsd[4] << 8) + (u16)vsd[5]; @@ -1455,7 +1550,9 @@ static void hdmi_edid_extract_dc(struct hdmi_edid_ctrl *edid_ctrl, if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) return; - edid_ctrl->deep_color = (vsd[6] >> 0x3) & 0xF; + edid_ctrl->deep_color = 0; + if (len >= 6) + edid_ctrl->deep_color = (vsd[6] >> 0x3) & 0xF; vsd = hdmi_edid_find_hfvsdb(in_buf); @@ -1650,7 +1747,12 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl, timing.pixel_freq = pixel_clk; timing.refresh_rate = refresh_rate; timing.interlaced = interlaced; - timing.supported = true; + if (!interlaced) + timing.supported = true; + + if (refresh_rate > (MAX_REFRESH_RATE_SUPPORTED * khz_to_hz)) + timing.supported = false; + timing.ar = aspect_ratio_4_3 ? HDMI_RES_AR_4_3 : (aspect_ratio_5_4 ? HDMI_RES_AR_5_4 : HDMI_RES_AR_16_9); @@ -1718,6 +1820,7 @@ static void hdmi_edid_add_sink_video_format(struct hdmi_edid_ctrl *edid_ctrl, struct hdmi_edid_sink_data *sink_data = &edid_ctrl->sink_data; struct disp_mode_info *disp_mode_list = sink_data->disp_mode_list; u32 i = 0; + bool y420_supported = false; if (video_format >= HDMI_VFRMT_MAX) { DEV_ERR("%s: video format: %s is not supported\n", __func__, @@ -1729,21 +1832,56 @@ static void hdmi_edid_add_sink_video_format(struct hdmi_edid_ctrl *edid_ctrl, video_format, msm_hdmi_mode_2string(video_format), supported ? "Supported" : "Not-Supported"); + if (edid_ctrl->y420_cmdb_present && video_format < HDMI_VFRMT_END) { + if (edid_ctrl->y420_cmdb_supports_all) { + y420_supported = true; + goto done; + } + + for (i = 0; i < edid_ctrl->y420_cmdb.len; i++) { + if (video_format == edid_ctrl->y420_cmdb.vic_list[i]) { + y420_supported = true; + break; + } + } + } + +done: + /* override the default resolution */ + if (edid_ctrl->override_default_vic) { + if (!ret && supported) { + disp_mode_list[0].video_format = video_format; + disp_mode_list[0].rgb_support = true; + if (y420_supported) + disp_mode_list[0].y420_support = true; + edid_ctrl->override_default_vic = false; + return; + } + } + for (i = 0; i < sink_data->num_of_elements; i++) { u32 vic = disp_mode_list[i].video_format; if (vic == video_format) { DEV_DBG("%s: vic %d already added\n", __func__, vic); + if (supported) + disp_mode_list[i].rgb_support = true; + if (y420_supported) + disp_mode_list[i].y420_support = true; return; } } - if (!ret && supported) { + if (!ret && (supported || y420_supported)) { /* todo: MHL */ disp_mode_list[sink_data->num_of_elements].video_format = video_format; - disp_mode_list[sink_data->num_of_elements].rgb_support = - true; + if (supported) + disp_mode_list[sink_data->num_of_elements]. + rgb_support = true; + if (y420_supported) + disp_mode_list[sink_data->num_of_elements]. + y420_support = true; sink_data->num_of_elements++; } } /* hdmi_edid_add_sink_video_format */ @@ -1914,6 +2052,12 @@ static void hdmi_edid_get_extended_video_formats( return; } + if (db_len < 8) { + DEV_DBG("%s: No extra Vendor Specific information present\n", + __func__); + return; + } + /* check if HDMI_Video_present flag is set or not */ if (!(vsd[8] & BIT(5))) { DEV_DBG("%s: extended vfmts are not supported by the sink.\n", @@ -2289,6 +2433,9 @@ int hdmi_edid_parser(void *input) u16 ieee_reg_id; int status = 0; u32 i = 0; + u32 cea_idx = 1; + u32 sink_caps_pclk_khz = 0; + u32 max_pclk_khz = 0; struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; if (!edid_ctrl) { @@ -2315,7 +2462,7 @@ int hdmi_edid_parser(void *input) /* EDID_CEA_EXTENSION_FLAG[0x7E] - CEC extension byte */ num_of_cea_blocks = edid_buf[EDID_BLOCK_SIZE - 2]; - DEV_DBG("%s: No. of CEA blocks is [%u]\n", __func__, + DEV_DBG("%s: No. of CEA/Extended EDID blocks is [%u]\n", __func__, num_of_cea_blocks); /* Find out any CEA extension blocks following block 0 */ @@ -2334,30 +2481,42 @@ int hdmi_edid_parser(void *input) num_of_cea_blocks = MAX_EDID_BLOCKS - 1; } - /* check for valid CEA block */ - if (edid_buf[EDID_BLOCK_SIZE] != 2) { - DEV_ERR("%s: Invalid CEA block\n", __func__); - num_of_cea_blocks = 0; - goto bail; + if (edid_buf[EDID_BLOCK_SIZE] == 0xF0) { + DEV_DBG("%s: Extended EDID Block Map found\n", __func__); + edid_buf += EDID_BLOCK_SIZE; + cea_idx++; } - /* goto to CEA extension edid block */ - edid_buf += EDID_BLOCK_SIZE; + for (i = cea_idx; i <= num_of_cea_blocks; i++) { - ieee_reg_id = hdmi_edid_extract_ieee_reg_id(edid_ctrl, edid_buf); - DEV_DBG("%s: ieee_reg_id = 0x%08x\n", __func__, ieee_reg_id); - if (ieee_reg_id == EDID_IEEE_REG_ID) - edid_ctrl->sink_mode = SINK_MODE_HDMI; - else - edid_ctrl->sink_mode = SINK_MODE_DVI; + /* check for valid CEA block */ + if (edid_buf[EDID_BLOCK_SIZE] != 2) { + DEV_ERR("%s: Not a CEA block\n", __func__); + edid_buf += EDID_BLOCK_SIZE; + continue; + } - hdmi_edid_extract_sink_caps(edid_ctrl, edid_buf); - hdmi_edid_extract_latency_fields(edid_ctrl, edid_buf); - hdmi_edid_extract_dc(edid_ctrl, edid_buf); - hdmi_edid_extract_speaker_allocation_data(edid_ctrl, edid_buf); - hdmi_edid_extract_audio_data_blocks(edid_ctrl, edid_buf); - hdmi_edid_extract_3d_present(edid_ctrl, edid_buf); - hdmi_edid_extract_extended_data_blocks(edid_ctrl, edid_buf); + /* goto to CEA extension edid block */ + edid_buf += EDID_BLOCK_SIZE; + + ieee_reg_id = hdmi_edid_extract_ieee_reg_id(edid_ctrl, + edid_buf); + DEV_DBG("%s: ieee_reg_id = 0x%06x\n", __func__, ieee_reg_id); + if (ieee_reg_id == EDID_IEEE_REG_ID) + edid_ctrl->sink_mode = SINK_MODE_HDMI; + else + edid_ctrl->sink_mode = SINK_MODE_DVI; + + if (ieee_reg_id == EDID_IEEE_REG_ID) { + hdmi_edid_extract_sink_caps(edid_ctrl, edid_buf); + hdmi_edid_extract_latency_fields(edid_ctrl, edid_buf); + hdmi_edid_extract_dc(edid_ctrl, edid_buf); + hdmi_edid_extract_3d_present(edid_ctrl, edid_buf); + } + hdmi_edid_extract_speaker_allocation_data(edid_ctrl, edid_buf); + hdmi_edid_extract_audio_data_blocks(edid_ctrl, edid_buf); + hdmi_edid_extract_extended_data_blocks(edid_ctrl, edid_buf); + } bail: for (i = 1; i <= num_of_cea_blocks; i++) { @@ -2369,6 +2528,13 @@ bail: edid_ctrl->cea_blks = num_of_cea_blocks; + sink_caps_pclk_khz = + hdmi_edid_get_sink_caps_max_tmds_clk(edid_ctrl) / 1000; + max_pclk_khz = hdmi_edid_get_max_pclk(edid_ctrl); + if (sink_caps_pclk_khz && max_pclk_khz) + hdmi_edid_set_max_pclk_rate(edid_ctrl, + min(max_pclk_khz, sink_caps_pclk_khz)); + hdmi_edid_get_display_mode(edid_ctrl); if (edid_ctrl->keep_resv_timings) @@ -2561,6 +2727,18 @@ void hdmi_edid_get_hdr_data(void *input, *hdr_data = &edid_ctrl->hdr_data; } +u8 hdmi_edid_get_colorimetry(void *input) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return 0; + } + + return edid_ctrl->colorimetry.standards; +} + bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode) { int i; @@ -2687,6 +2865,7 @@ void hdmi_edid_set_video_resolution(void *input, u32 resolution, bool reset) edid_ctrl->sink_data.disp_mode_list[0].video_format = resolution; edid_ctrl->sink_data.disp_mode_list[0].rgb_support = true; + edid_ctrl->override_default_vic = true; } } /* hdmi_edid_set_video_resolution */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index 63785e95bd59..ae4f47ca42aa 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -20,6 +20,15 @@ #define EDID_BLOCK_ADDR 0xA0 #define MAX_EDID_BLOCKS 5 +#define EDID_COLORIMETRY_xvYCC_601 (1 << 0) +#define EDID_COLORIMETRY_xvYCC_709 (1 << 1) +#define EDID_COLORIMETRY_sYCC_601 (1 << 2) +#define EDID_COLORIMETRY_ADBYCC_601 (1 << 3) +#define EDID_COLORIMETRY_ADB_RGB (1 << 4) +#define EDID_COLORIMETRY_BT2020_CYCC (1 << 5) +#define EDID_COLORIMETRY_BT2020_YCC (1 << 6) +#define EDID_COLORIMETRY_BT2020_RGB (1 << 7) + struct hdmi_edid_init_data { struct kobject *kobj; struct hdmi_util_ds_data ds_data; @@ -83,5 +92,6 @@ void hdmi_edid_config_override(void *input, bool enable, void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz); bool hdmi_edid_is_audio_supported(void *input); u32 hdmi_edid_get_sink_caps_max_tmds_clk(void *input); +u8 hdmi_edid_get_colorimetry(void *input); #endif /* __HDMI_EDID_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c index 46e289b6dbd3..b922662d25aa 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -393,31 +393,22 @@ exit: return count; } -static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static void mdss_hdmi_hdcp2p2_min_level_change(void *client_ctx, + int min_enc_level) { struct hdmi_hdcp2p2_ctrl *ctrl = - hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); + (struct hdmi_hdcp2p2_ctrl *)client_ctx; struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE}; bool enc_notify = true; enum hdcp_states enc_lvl; - int min_enc_lvl; - int rc; if (!ctrl) { pr_err("invalid input\n"); - rc = -EINVAL; - goto exit; + return; } - rc = kstrtoint(buf, 10, &min_enc_lvl); - if (rc) { - DEV_ERR("%s: kstrtoint failed. rc=%d\n", __func__, rc); - goto exit; - } - - switch (min_enc_lvl) { + switch (min_enc_level) { case 0: enc_lvl = HDCP_STATE_AUTH_ENC_NONE; break; @@ -431,7 +422,7 @@ static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev, enc_notify = false; } - pr_debug("enc level changed %d\n", min_enc_lvl); + pr_debug("enc level changed %d\n", min_enc_level); cdata.context = ctrl->lib_ctx; hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); @@ -441,10 +432,6 @@ static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev, if (enc_notify && ctrl->init_data.notify_status) ctrl->init_data.notify_status(ctrl->init_data.cb_data, enc_lvl); - - rc = count; -exit: - return rc; } static void hdmi_hdcp2p2_auth_failed(struct hdmi_hdcp2p2_ctrl *ctrl) @@ -584,13 +571,10 @@ static int hdmi_hdcp2p2_read_version(struct hdmi_hdcp2p2_ctrl *ctrl, return rc; } -static DEVICE_ATTR(min_level_change, S_IWUSR, NULL, - hdmi_hdcp2p2_sysfs_wta_min_level_change); static DEVICE_ATTR(tethered, S_IRUGO | S_IWUSR, hdmi_hdcp2p2_sysfs_rda_tethered, hdmi_hdcp2p2_sysfs_wta_tethered); static struct attribute *hdmi_hdcp2p2_fs_attrs[] = { - &dev_attr_min_level_change.attr, &dev_attr_tethered.attr, NULL, }; @@ -1030,6 +1014,7 @@ void *hdmi_hdcp2p2_init(struct hdcp_init_data *init_data) static struct hdcp_client_ops client_ops = { .wakeup = hdmi_hdcp2p2_wakeup, + .notify_lvl_change = mdss_hdmi_hdcp2p2_min_level_change, .srm_cb = hdmi_hdcp2p2_srm_cb, }; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_panel.c b/drivers/video/fbdev/msm/mdss_hdmi_panel.c index af72973a3988..20afa08155c1 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_panel.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_panel.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -137,12 +137,36 @@ enum { DATA_BYTE_13, }; +enum hdmi_colorimetry { + HDMI_COLORIMETRY_DEFAULT, + HDMI_COLORIMETRY_ITU_R_601, + HDMI_COLORIMETRY_ITU_R_709, + HDMI_COLORIMETRY_EXTENDED +}; + +enum hdmi_ext_colorimetry { + HDMI_COLORIMETRY_XV_YCC_601, + HDMI_COLORIMETRY_XV_YCC_709, + HDMI_COLORIMETRY_S_YCC_601, + HDMI_COLORIMETRY_ADOBE_YCC_601, + HDMI_COLORIMETRY_ADOBE_RGB, + HDMI_COLORIMETRY_C_YCBCR_BT2020, + HDMI_COLORIMETRY_YCBCR_BT2020, + HDMI_COLORIMETRY_RESERVED + +}; + enum hdmi_quantization_range { HDMI_QUANTIZATION_DEFAULT, HDMI_QUANTIZATION_LIMITED_RANGE, HDMI_QUANTIZATION_FULL_RANGE }; +enum hdmi_ycc_quantization_range { + HDMI_YCC_QUANTIZATION_LIMITED_RANGE, + HDMI_YCC_QUANTIZATION_FULL_RANGE +}; + enum hdmi_scaling_info { HDMI_SCALING_NONE, HDMI_SCALING_HORZ, @@ -189,12 +213,29 @@ static int hdmi_panel_config_avi(struct hdmi_panel *panel) avi->bar_info.start_of_right_bar = timing->active_h + 1; avi->act_fmt_info_present = true; - avi->rgb_quantization_range = HDMI_QUANTIZATION_DEFAULT; - avi->yuv_quantization_range = HDMI_QUANTIZATION_DEFAULT; + if (pinfo->is_ce_mode) { + avi->rgb_quantization_range = + HDMI_QUANTIZATION_LIMITED_RANGE; + avi->yuv_quantization_range = + HDMI_YCC_QUANTIZATION_LIMITED_RANGE; + } else { + avi->rgb_quantization_range = + HDMI_QUANTIZATION_FULL_RANGE; + avi->yuv_quantization_range = + HDMI_YCC_QUANTIZATION_FULL_RANGE; + } avi->scaling_info = HDMI_SCALING_NONE; - avi->colorimetry_info = 0; + if (avi->pixel_format == MDP_Y_CBCR_H2V2) { + if (pinfo->yres < 720) + avi->colorimetry_info = HDMI_COLORIMETRY_ITU_R_601; + else + avi->colorimetry_info = HDMI_COLORIMETRY_ITU_R_709; + } else { + avi->colorimetry_info = HDMI_COLORIMETRY_DEFAULT; + } + avi->ext_colorimetry_info = 0; avi->pixel_rpt_factor = 0; @@ -602,13 +643,29 @@ end: return rc; } +static inline int get_bitdepth(enum hdmi_deep_color_depth bitdepth) +{ + switch (bitdepth) { + case HDMI_DEEP_COLOR_DEPTH_24BPP: + return 24; + case HDMI_DEEP_COLOR_DEPTH_30BPP: + return 30; + case HDMI_DEEP_COLOR_DEPTH_36BPP: + return 36; + default: + return 0; + } +} + static int hdmi_panel_setup_dc(struct hdmi_panel *panel) { u32 hdmi_ctrl_reg; u32 vbi_pkt_reg; int rc = 0; - pr_debug("Deep Color: %s\n", panel->data->dc_enable ? "ON" : "OFF"); + pr_debug("Deep Color: %s, bitdepth = %d\n", + panel->data->dc_enable ? "ON" : "OFF", + get_bitdepth(panel->data->bitdepth)); /* enable deep color if supported */ if (panel->data->dc_enable) { @@ -789,6 +846,53 @@ end: return panel->vic; } +static int hdmi_panel_avi_update_colorimetry(void *input, + bool use_bt2020) +{ + struct hdmi_panel *panel = input; + struct mdss_panel_info *pinfo; + struct hdmi_video_config *vid_cfg; + struct hdmi_avi_infoframe_config *avi; + int rc = 0; + + if (!panel) { + DEV_ERR("%s: invalid hdmi panel\n", __func__); + rc = -EINVAL; + goto error; + } + + /* Configure AVI infoframe */ + rc = hdmi_panel_config_avi(panel); + if (rc) { + DEV_ERR("%s: failed to configure AVI\n", __func__); + goto error; + } + + pinfo = panel->data->pinfo; + vid_cfg = &panel->vid_cfg; + avi = &vid_cfg->avi_iframe; + + /* Update Colorimetry */ + avi->ext_colorimetry_info = 0; + + if (use_bt2020) { + avi->colorimetry_info = HDMI_COLORIMETRY_EXTENDED; + avi->ext_colorimetry_info = HDMI_COLORIMETRY_YCBCR_BT2020; + } else if (avi->pixel_format == MDP_Y_CBCR_H2V2) { + if (pinfo->yres < 720) + avi->colorimetry_info = HDMI_COLORIMETRY_ITU_R_601; + else + avi->colorimetry_info = HDMI_COLORIMETRY_ITU_R_709; + } else { + avi->colorimetry_info = HDMI_COLORIMETRY_DEFAULT; + } + + hdmi_panel_set_avi_infoframe(panel); + +error: + return rc; +} + static int hdmi_panel_power_on(void *input) { int rc = 0; @@ -915,6 +1019,8 @@ void *hdmi_panel_init(struct hdmi_panel_init_data *data) data->ops->off = hdmi_panel_power_off; data->ops->vendor = hdmi_panel_set_vendor_specific_infoframe; data->ops->update_fps = hdmi_panel_update_fps; + data->ops->update_colorimetry = + hdmi_panel_avi_update_colorimetry; } end: return panel; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_panel.h b/drivers/video/fbdev/msm/mdss_hdmi_panel.h index cb40f0cad55e..04cd971e2db0 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_panel.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_panel.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -16,6 +16,13 @@ #include "mdss_panel.h" #include "mdss_hdmi_util.h" +enum hdmi_deep_color_depth { + HDMI_DEEP_COLOR_DEPTH_24BPP, + HDMI_DEEP_COLOR_DEPTH_30BPP, + HDMI_DEEP_COLOR_DEPTH_36BPP, + HDMI_DEEP_COLOR_DEPTH_RESERVED, +}; + /** * struct hdmi_panel_data - panel related data information * @@ -29,6 +36,7 @@ * @is_it_content: set to true if content is IT * @scrambler: set to true if scrambler needs to be enabled * @dc_enable: set to true if deep color is enabled + * @bitdepth: set the output bitdepth like 24/30 bpp */ struct hdmi_panel_data { struct mdss_panel_info *pinfo; @@ -41,6 +49,7 @@ struct hdmi_panel_data { bool is_it_content; bool scrambler; bool dc_enable; + enum hdmi_deep_color_depth bitdepth; }; /** @@ -56,6 +65,7 @@ struct hdmi_panel_ops { int (*off)(void *input); void (*vendor)(void *input); int (*update_fps)(void *input, u32 fps); + int (*update_colorimetry)(void *input, bool use_bt2020); }; /** diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 0778e43fe0ff..79a6a16d4fed 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, 2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -122,6 +122,11 @@ static int hdmi_tx_get_cable_status(struct platform_device *pdev, u32 vote); static int hdmi_tx_update_ppm(struct hdmi_tx_ctrl *hdmi_ctrl, s32 ppm); static int hdmi_tx_enable_pll_update(struct hdmi_tx_ctrl *hdmi_ctrl, int enable); +static void hdmi_tx_hpd_polarity_setup(struct hdmi_tx_ctrl *hdmi_ctrl, + bool polarity); +static int hdmi_tx_notify_events(struct hdmi_tx_ctrl *hdmi_ctrl, int val); +static void hdmi_panel_update_colorimetry(struct hdmi_tx_ctrl *ctrl, + bool use_bt2020); static struct mdss_hw hdmi_tx_hw = { .hw_ndx = MDSS_HW_HDMI, @@ -305,7 +310,7 @@ static inline bool hdmi_tx_metadata_type_one(struct hdmi_tx_ctrl *hdmi_ctrl) return hdr_data->metadata_type_one; } -static inline bool hdmix_tx_sink_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl) +static inline bool hdmi_tx_sink_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl) { void *edid_fd = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID); @@ -325,8 +330,7 @@ static inline bool hdmi_tx_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl) true); return hdmi_ctrl->dc_feature_on && - hdmi_ctrl->dc_support && - hdmix_tx_sink_dc_support(hdmi_ctrl) && + hdmi_tx_sink_dc_support(hdmi_ctrl) && (tmds_clk_with_dc <= hdmi_edid_get_max_pclk(edid_fd)); } @@ -429,9 +433,17 @@ static inline void hdmi_tx_send_audio_notification( } } -static inline void hdmi_tx_send_video_notification( +static inline int hdmi_tx_send_video_notification( struct hdmi_tx_ctrl *hdmi_ctrl, int val, bool async) { + int ret = 0; + + if (!hdmi_ctrl) { + pr_err("invalid hdmi_ctrl input\n"); + ret = -EINVAL; + goto end; + } + if (hdmi_ctrl && hdmi_ctrl->ext_audio_data.intf_ops.hpd) { u32 flags = 0; @@ -440,9 +452,13 @@ static inline void hdmi_tx_send_video_notification( else flags |= MSM_EXT_DISP_HPD_VIDEO; - hdmi_ctrl->ext_audio_data.intf_ops.hpd(hdmi_ctrl->ext_pdev, - hdmi_ctrl->ext_audio_data.type, val, flags); + ret = hdmi_ctrl->ext_audio_data.intf_ops.hpd( + hdmi_ctrl->ext_pdev, + hdmi_ctrl->ext_audio_data.type, + val, flags); } +end: + return ret; } static inline void hdmi_tx_ack_state( @@ -548,8 +564,8 @@ static ssize_t hdmi_tx_sysfs_rda_connected(struct device *dev, } mutex_lock(&hdmi_ctrl->tx_lock); - ret = snprintf(buf, PAGE_SIZE, "%d\n", hdmi_ctrl->hpd_state); - DEV_DBG("%s: '%d'\n", __func__, hdmi_ctrl->hpd_state); + ret = snprintf(buf, PAGE_SIZE, "%d\n", hdmi_ctrl->notification_status); + DEV_DBG("%s: '%d'\n", __func__, hdmi_ctrl->notification_status); mutex_unlock(&hdmi_ctrl->tx_lock); return ret; @@ -883,8 +899,7 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev, * No need to blocking wait for display/audio in this * case since HAL is not up so no ACK can be expected. */ - hdmi_tx_send_audio_notification(hdmi_ctrl, 0); - hdmi_tx_send_video_notification(hdmi_ctrl, 0, true); + hdmi_tx_notify_events(hdmi_ctrl, 0); } break; @@ -1329,9 +1344,13 @@ static ssize_t hdmi_tx_sysfs_wta_hdr_stream(struct device *dev, hdr_op = hdmi_hdr_get_ops(ctrl->curr_hdr_state, ctrl->hdr_ctrl.hdr_state); - if (hdr_op == HDR_SEND_INFO) + if (hdr_op == HDR_SEND_INFO) { hdmi_panel_set_hdr_infoframe(ctrl); - else if (hdr_op == HDR_CLEAR_INFO) + if (ctrl->hdr_ctrl.hdr_stream.eotf) + hdmi_panel_update_colorimetry(ctrl, true); + else + hdmi_panel_update_colorimetry(ctrl, false); + } else if (hdr_op == HDR_CLEAR_INFO) hdmi_panel_clear_hdr_infoframe(ctrl); ctrl->curr_hdr_state = ctrl->hdr_ctrl.hdr_state; @@ -1848,7 +1867,6 @@ static int hdmi_tx_read_edid(struct hdmi_tx_ctrl *hdmi_ctrl) } } while ((cea_blks-- > 0) && (block++ < MAX_EDID_BLOCKS)); end: - return ret; } @@ -2429,11 +2447,60 @@ static void hdmi_tx_update_hdr_info(struct hdmi_tx_ctrl *hdmi_ctrl) } } +static int hdmi_tx_notify_events(struct hdmi_tx_ctrl *hdmi_ctrl, int val) +{ + int rc = 0; + + if (val == hdmi_ctrl->notification_status) { + pr_debug("%s: No change in notification status %d -> %d\n", + __func__, hdmi_ctrl->notification_status, val); + goto end; + } + + reinit_completion(&hdmi_ctrl->notification_comp); + if (atomic_read(&hdmi_ctrl->notification_pending)) { + pr_debug("%s: wait for previous event to finish\n", __func__); + rc = wait_for_completion_timeout( + &hdmi_ctrl->notification_comp, HZ); + if (rc <= 0) { + pr_debug("%s: wait for pending notification timed out\n", + __func__); + hdmi_ctrl->pending_event = val; + hdmi_ctrl->handle_pe = true; + rc = -ETIMEDOUT; + goto end; + } + } + + hdmi_ctrl->notification_status = val; + atomic_set(&hdmi_ctrl->notification_pending, 1); + + if (val) { + rc = hdmi_tx_send_video_notification(hdmi_ctrl, val, true); + } else { + hdmi_tx_send_audio_notification(hdmi_ctrl, val); + rc = hdmi_tx_send_video_notification(hdmi_ctrl, val, true); + } + + if (!rc) { + pr_debug("%s: Successfully sent %s notification\n", __func__, + val ? "CONNECT" : "DISCONNECT"); + } else { + pr_err("%s: %s notification failed\n", __func__, + val ? "CONNECT" : "DISCONNECT"); + atomic_set(&hdmi_ctrl->notification_pending, 0); + } +end: + return rc; +} + static void hdmi_tx_hpd_int_work(struct work_struct *work) { struct hdmi_tx_ctrl *hdmi_ctrl = NULL; int rc = -EINVAL; int retry = MAX_EDID_READ_RETRY; + unsigned long flags; + u32 hpd_state; hdmi_ctrl = container_of(work, struct hdmi_tx_ctrl, hpd_int_work); if (!hdmi_ctrl) { @@ -2441,36 +2508,39 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work) return; } - mutex_lock(&hdmi_ctrl->tx_lock); - if (!hdmi_ctrl->hpd_initialized) { DEV_DBG("hpd not initialized\n"); - mutex_unlock(&hdmi_ctrl->tx_lock); return; } - DEV_DBG("%s: %s\n", __func__, - hdmi_ctrl->hpd_state ? "CONNECT" : "DISCONNECT"); + spin_lock_irqsave(&hdmi_ctrl->hpd_state_lock, flags); + hpd_state = hdmi_ctrl->hpd_state; + spin_unlock_irqrestore(&hdmi_ctrl->hpd_state_lock, flags); + + DEV_DBG("%s: %s\n", __func__, + hpd_state ? "CONNECT" : "DISCONNECT"); + + if (hpd_state) { + hdmi_tx_hpd_polarity_setup(hdmi_ctrl, + HPD_DISCONNECT_POLARITY); - if (hdmi_ctrl->hpd_state) { while (rc && retry--) rc = hdmi_tx_read_sink_info(hdmi_ctrl); if (!retry && rc) pr_warn_ratelimited("%s: EDID read failed\n", __func__); hdmi_tx_update_deep_color(hdmi_ctrl); hdmi_tx_update_hdr_info(hdmi_ctrl); - } - mutex_unlock(&hdmi_ctrl->tx_lock); + hdmi_tx_notify_events(hdmi_ctrl, hpd_state); - if (hdmi_ctrl->hpd_state) - hdmi_tx_send_video_notification(hdmi_ctrl, - hdmi_ctrl->hpd_state, true); - else { - hdmi_tx_send_audio_notification(hdmi_ctrl, - hdmi_ctrl->hpd_state); - hdmi_tx_send_video_notification(hdmi_ctrl, - hdmi_ctrl->hpd_state, true); + } else { + rc = hdmi_tx_notify_events(hdmi_ctrl, hpd_state); + + if (!rc && !hdmi_ctrl->panel_power_on) { + atomic_set(&hdmi_ctrl->notification_pending, 0); + hdmi_tx_hpd_polarity_setup(hdmi_ctrl, + HPD_CONNECT_POLARITY); + } } } /* hdmi_tx_hpd_int_work */ @@ -3065,6 +3135,34 @@ static void hdmi_panel_clear_hdr_infoframe(struct hdmi_tx_ctrl *ctrl) DSS_REG_W(io, HDMI_GEN_PKT_CTRL, packet_control); } +static void hdmi_panel_update_colorimetry(struct hdmi_tx_ctrl *hdmi_ctrl, + bool use_bt2020) +{ + void *pdata; + + if (!hdmi_ctrl) { + DEV_ERR("%s: invalid hdmi ctrl data\n", __func__); + return; + } + + pdata = hdmi_tx_get_fd(HDMI_TX_FEAT_PANEL); + if (!pdata) { + DEV_ERR("%s: invalid panel data\n", __func__); + return; + } + + /* If there is no change in colorimetry, just return */ + if (use_bt2020 && hdmi_ctrl->use_bt2020) + return; + else if (!use_bt2020 && !hdmi_ctrl->use_bt2020) + return; + + if (hdmi_ctrl->panel_ops.update_colorimetry) + hdmi_ctrl->panel_ops.update_colorimetry(pdata, use_bt2020); + + hdmi_ctrl->use_bt2020 = use_bt2020; +} + static int hdmi_tx_audio_info_setup(struct platform_device *pdev, struct msm_ext_disp_audio_setup_params *params) { @@ -3289,7 +3387,7 @@ static int hdmi_tx_power_off(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_tx_core_off(hdmi_ctrl); hdmi_ctrl->panel_power_on = false; - hdmi_ctrl->dc_support = false; + hdmi_ctrl->vic = 0; if (hdmi_ctrl->hpd_off_pending || hdmi_ctrl->panel_suspend) hdmi_tx_hpd_off(hdmi_ctrl); @@ -3337,6 +3435,10 @@ static int hdmi_tx_power_on(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->panel.scrambler = hdmi_edid_get_sink_scrambler_support( edata); hdmi_ctrl->panel.dc_enable = hdmi_tx_dc_support(hdmi_ctrl); + if (hdmi_ctrl->panel.dc_enable) + hdmi_ctrl->panel.bitdepth = HDMI_DEEP_COLOR_DEPTH_30BPP; + else + hdmi_ctrl->panel.bitdepth = HDMI_DEEP_COLOR_DEPTH_24BPP; if (hdmi_ctrl->panel_ops.on) hdmi_ctrl->panel_ops.on(pdata); @@ -3362,8 +3464,6 @@ static int hdmi_tx_power_on(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->panel_power_on = true; - hdmi_tx_hpd_polarity_setup(hdmi_ctrl, HPD_DISCONNECT_POLARITY); - if (hdmi_ctrl->hdmi_tx_hpd_done) hdmi_ctrl->hdmi_tx_hpd_done(hdmi_ctrl->downstream_data); @@ -3415,6 +3515,7 @@ static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->hpd_initialized = false; hdmi_ctrl->hpd_off_pending = false; + hdmi_ctrl->dc_support = false; DEV_DBG("%s: HPD is now OFF\n", __func__); } /* hdmi_tx_hpd_off */ @@ -3473,6 +3574,7 @@ static int hdmi_tx_hpd_on(struct hdmi_tx_ctrl *hdmi_ctrl) /* Turn on HPD HW circuit */ DSS_REG_W(io, HDMI_HPD_CTRL, reg_val | BIT(28)); + atomic_set(&hdmi_ctrl->notification_pending, 0); hdmi_tx_hpd_polarity_setup(hdmi_ctrl, HPD_CONNECT_POLARITY); DEV_DBG("%s: HPD is now ON\n", __func__); } @@ -3697,6 +3799,11 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl) spin_lock_init(&hdmi_ctrl->hpd_state_lock); + hdmi_ctrl->pending_event = 0; + hdmi_ctrl->handle_pe = false; + atomic_set(&hdmi_ctrl->notification_pending, 0); + init_completion(&hdmi_ctrl->notification_comp); + return 0; fail_create_workq: @@ -3972,9 +4079,11 @@ sysfs_err: static int hdmi_tx_evt_handle_check_param(struct hdmi_tx_ctrl *hdmi_ctrl) { + struct mdss_panel_info *pinfo = &hdmi_ctrl->panel_data.panel_info; int new_vic = -1; int rc = 0; + pinfo->is_ce_mode = false; new_vic = hdmi_panel_get_vic(hdmi_ctrl->evt_arg, &hdmi_ctrl->ds_data); if ((new_vic < 0) || (new_vic > HDMI_VFRMT_MAX)) { @@ -3991,7 +4100,20 @@ static int hdmi_tx_evt_handle_check_param(struct hdmi_tx_ctrl *hdmi_ctrl) rc = 1; DEV_DBG("%s: res change %d ==> %d\n", __func__, hdmi_ctrl->vic, new_vic); + goto done; } + + /* + * Since bootloader doesn't support DC return 1 + * for panel reconfig. + */ + if (hdmi_ctrl->panel_data.panel_info.cont_splash_enabled + && hdmi_tx_dc_support(hdmi_ctrl)) { + rc = 1; + DEV_DBG("%s: Bitdepth changed\n", __func__); + } +done: + pinfo->is_ce_mode = hdmi_util_is_ce_mode(new_vic); end: return rc; } @@ -4012,6 +4134,9 @@ static int hdmi_tx_evt_handle_resume(struct hdmi_tx_ctrl *hdmi_ctrl) goto end; } + if (hdmi_tx_is_cec_wakeup_en(hdmi_ctrl)) + hdmi_ctrl->mdss_util->disable_wake_irq(&hdmi_tx_hw); + end: return rc; } @@ -4070,6 +4195,9 @@ static int hdmi_tx_evt_handle_suspend(struct hdmi_tx_ctrl *hdmi_ctrl) if (!hdmi_ctrl->hpd_state && !hdmi_ctrl->panel_power_on) hdmi_tx_hpd_off(hdmi_ctrl); + if (hdmi_tx_is_cec_wakeup_en(hdmi_ctrl)) + hdmi_ctrl->mdss_util->enable_wake_irq(&hdmi_tx_hw); + hdmi_ctrl->panel_suspend = true; hdmi_tx_cec_device_suspend(hdmi_ctrl); end: @@ -4113,8 +4241,12 @@ end: static int hdmi_tx_evt_handle_close(struct hdmi_tx_ctrl *hdmi_ctrl) { if (hdmi_ctrl->hpd_feature_on && hdmi_ctrl->hpd_initialized && - !hdmi_ctrl->hpd_state) + !hdmi_ctrl->notification_status) { + atomic_set(&hdmi_ctrl->notification_pending, 0); + complete_all(&hdmi_ctrl->notification_comp); + hdmi_tx_hpd_polarity_setup(hdmi_ctrl, HPD_CONNECT_POLARITY); + } return 0; } @@ -4166,6 +4298,15 @@ static int hdmi_tx_post_evt_handle_unblank(struct hdmi_tx_ctrl *hdmi_ctrl) { hdmi_tx_ack_state(hdmi_ctrl, true); hdmi_tx_send_audio_notification(hdmi_ctrl, true); + + atomic_set(&hdmi_ctrl->notification_pending, 0); + complete_all(&hdmi_ctrl->notification_comp); + + if (hdmi_ctrl->handle_pe) { + hdmi_ctrl->handle_pe = false; + hdmi_tx_notify_events(hdmi_ctrl, hdmi_ctrl->pending_event); + } + return 0; } @@ -4182,8 +4323,7 @@ static int hdmi_tx_post_evt_handle_resume(struct hdmi_tx_ctrl *hdmi_ctrl) &hdmi_ctrl->hpd_int_done, HZ/10); if (!timeout) { pr_debug("cable removed during suspend\n"); - hdmi_tx_send_audio_notification(hdmi_ctrl, 0); - hdmi_tx_send_video_notification(hdmi_ctrl, 0, true); + hdmi_tx_notify_events(hdmi_ctrl, 0); } } @@ -4194,8 +4334,7 @@ static int hdmi_tx_post_evt_handle_panel_on(struct hdmi_tx_ctrl *hdmi_ctrl) { if (hdmi_ctrl->panel_suspend) { pr_debug("panel suspend has triggered\n"); - hdmi_tx_send_audio_notification(hdmi_ctrl, 0); - hdmi_tx_send_video_notification(hdmi_ctrl, 0, true); + hdmi_tx_notify_events(hdmi_ctrl, 0); } return 0; @@ -4259,6 +4398,38 @@ static int hdmi_tx_event_handler(struct mdss_panel_data *panel_data, return rc; } +static enum mdss_mdp_csc_type mdss_hdmi_get_csc_type( + struct mdss_panel_data *panel_data) +{ + struct mdss_panel_info *pinfo; + struct mdp_hdr_stream_ctrl *hdr_ctrl; + struct mdp_hdr_stream *hdr_data; + enum mdss_mdp_csc_type csc_type = MDSS_MDP_CSC_RGB2YUV_709L; + + struct hdmi_tx_ctrl *hdmi_ctrl = + hdmi_tx_get_drvdata_from_panel_data(panel_data); + + if (!hdmi_ctrl) { + DEV_ERR("%s: invalid hdmi ctrl data\n", __func__); + goto error; + } + + pinfo = &hdmi_ctrl->panel_data.panel_info; + hdr_ctrl = &hdmi_ctrl->hdr_ctrl; + hdr_data = &hdr_ctrl->hdr_stream; + + if ((hdr_ctrl->hdr_state == HDR_ENABLE) && + (hdr_data->eotf != 0)) + csc_type = MDSS_MDP_CSC_RGB2YUV_2020L; + else if (pinfo->is_ce_mode) + csc_type = MDSS_MDP_CSC_RGB2YUV_709L; + else + csc_type = MDSS_MDP_CSC_RGB2YUV_709FR; + +error: + return csc_type; +} + static int hdmi_tx_register_panel(struct hdmi_tx_ctrl *hdmi_ctrl) { int rc = 0; @@ -4269,6 +4440,7 @@ static int hdmi_tx_register_panel(struct hdmi_tx_ctrl *hdmi_ctrl) } hdmi_ctrl->panel_data.event_handler = hdmi_tx_event_handler; + hdmi_ctrl->panel_data.get_csc_type = mdss_hdmi_get_csc_type; if (!hdmi_ctrl->pdata.primary) hdmi_ctrl->vic = DEFAULT_VIDEO_RESOLUTION; @@ -5005,6 +5177,7 @@ static int hdmi_tx_probe(struct platform_device *pdev) hdmi_ctrl->pdata.primary = true; hdmi_ctrl->vic = vic; hdmi_ctrl->panel_data.panel_info.is_prim_panel = true; + hdmi_ctrl->panel_data.panel_info.is_ce_mode = true; hdmi_ctrl->panel_data.panel_info.cont_splash_enabled = hdmi_ctrl->mdss_util->panel_intf_status(DISPLAY_1, MDSS_PANEL_INTF_HDMI) ? true : false; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index 92b9d84e9107..7eb749fde97d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -93,6 +93,12 @@ struct hdmi_tx_ctrl { struct work_struct fps_work; struct mdp_hdr_stream_ctrl hdr_ctrl; + int pending_event; + bool handle_pe; + atomic_t notification_pending; + struct completion notification_comp; + u32 notification_status; + spinlock_t hpd_state_lock; u32 panel_power_on; @@ -132,6 +138,7 @@ struct hdmi_tx_ctrl { bool power_data_enable[HDMI_TX_MAX_PM]; bool dc_support; bool dc_feature_on; + bool use_bt2020; void (*hdmi_tx_hpd_done)(void *data); void *downstream_data; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c index 8e854c54bae8..88bceabc4783 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, 2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -566,6 +566,67 @@ int msm_hdmi_get_timing_info( return ret; } +static u32 cea_mode_alternate_clock(struct msm_hdmi_mode_timing_info *info) +{ + u32 clock = info->pixel_freq; + + if (info->refresh_rate % 6 != 0) + return clock; + + clock = DIV_ROUND_CLOSEST(clock * 1000, 1001); + + return clock; +} + +bool hdmi_util_is_ce_mode(u32 vic) +{ + struct msm_hdmi_mode_timing_info info1 = {0}; + struct msm_hdmi_mode_timing_info info2 = {0}; + u32 mode = 0; + u32 clock1, clock2, clock2_alt; + bool is_ce_mode = false; + + if (vic <= HDMI_VFRMT_640x480p60_4_3) { + is_ce_mode = false; + goto end; + } + + if (vic >= HDMI_VFRMT_720x480p60_4_3 && + vic <= HDMI_VFRMT_3840x2160p60_64_27) { + is_ce_mode = true; + goto end; + } + + msm_hdmi_get_timing_info(&info1, vic); + + for (mode = HDMI_VFRMT_720x480p60_4_3; mode < HDMI_VFRMT_END; mode++) { + + msm_hdmi_get_timing_info(&info2, mode); + + clock1 = info1.pixel_freq; + clock2 = info2.pixel_freq; + clock2_alt = cea_mode_alternate_clock(&info2); + + if ((clock1 == clock2) || (clock1 == clock2_alt)) { + if (info1.active_h == info2.active_h && + info1.front_porch_h == info2.front_porch_h && + info1.pulse_width_h == info2.pulse_width_h && + info1.back_porch_h == info2.back_porch_h && + info1.active_v == info2.active_v && + info1.front_porch_v == info2.front_porch_v && + info1.pulse_width_v == info2.pulse_width_v && + info1.back_porch_v == info2.back_porch_v) { + is_ce_mode = true; + break; + } + } + continue; + } +end: + pr_debug("%s: vic = %d, is_ce_mode = %d\n", __func__, vic, is_ce_mode); + return is_ce_mode; +} + int hdmi_get_supported_mode(struct msm_hdmi_mode_timing_info *info, struct hdmi_util_ds_data *ds_data, u32 mode) { diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.h b/drivers/video/fbdev/msm/mdss_hdmi_util.h index fe554f8e9e67..2f752dc79c7a 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -490,6 +490,7 @@ static inline int hdmi_tx_get_h_total(const struct msm_hdmi_mode_timing_info *t) return 0; } +bool hdmi_util_is_ce_mode(u32 vic); /* video timing related utility routines */ int hdmi_get_video_id_code(struct msm_hdmi_mode_timing_info *timing_in, struct hdmi_util_ds_data *ds_data); diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index a77e5c4435bb..98172233b5aa 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1,7 +1,7 @@ /* * MDSS MDP Interface (used by framebuffer core) * - * Copyright (c) 2007-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2007-2019, The Linux Foundation. All rights reserved. * Copyright (C) 2007 Google Incorporated * * This software is licensed under the terms of the GNU General Public @@ -3117,7 +3117,8 @@ static int mdss_mdp_probe(struct platform_device *pdev) MDSS_MDP_REG_SPLIT_DISPLAY_EN); if (intf_sel != 0) { for (i = 0; i < 4; i++) - num_of_display_on += ((intf_sel >> i*8) & 0x000000FF); + num_of_display_on += + (((intf_sel >> i*8) & 0x000000FF) ? 1 : 0); /* * For split display enabled - DSI0, DSI1 interfaces are diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 90a38b79e561..9c93c500d127 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -271,10 +271,12 @@ enum mdss_mdp_csc_type { MDSS_MDP_CSC_RGB2YUV_601L, MDSS_MDP_CSC_RGB2YUV_601FR, MDSS_MDP_CSC_RGB2YUV_709L, + MDSS_MDP_CSC_RGB2YUV_709FR, MDSS_MDP_CSC_RGB2YUV_2020L, MDSS_MDP_CSC_RGB2YUV_2020FR, MDSS_MDP_CSC_YUV2YUV, MDSS_MDP_CSC_RGB2RGB, + MDSS_MDP_CSC_RGB2RGB_L, MDSS_MDP_MAX_CSC }; diff --git a/drivers/video/fbdev/msm/mdss_mdp_cdm.c b/drivers/video/fbdev/msm/mdss_mdp_cdm.c index 10928e6bceaa..7fede8fa1709 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_cdm.c +++ b/drivers/video/fbdev/msm/mdss_mdp_cdm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -116,6 +116,7 @@ struct mdss_mdp_cdm *mdss_mdp_cdm_init(struct mdss_mdp_ctl *ctl, u32 intf_type) cdm->out_intf = intf_type; cdm->is_bypassed = true; + cdm->is_only_clamped = false; memset(&cdm->setup, 0x0, sizeof(struct mdp_cdm_cfg)); return cdm; @@ -137,11 +138,22 @@ static int mdss_mdp_cdm_csc_setup(struct mdss_mdp_cdm *cdm, if ((data->csc_type == MDSS_MDP_CSC_RGB2YUV_601L) || (data->csc_type == MDSS_MDP_CSC_RGB2YUV_601FR) || - (data->csc_type == MDSS_MDP_CSC_RGB2YUV_709L)) { + (data->csc_type == MDSS_MDP_CSC_RGB2YUV_709L) || + (data->csc_type == MDSS_MDP_CSC_RGB2YUV_709FR) || + (data->csc_type == MDSS_MDP_CSC_RGB2YUV_2020L) || + (data->csc_type == MDSS_MDP_CSC_RGB2YUV_2020FR)) { op_mode |= BIT(2); /* DST_DATA_FORMAT = YUV */ op_mode &= ~BIT(1); /* SRC_DATA_FORMAT = RGB */ op_mode |= BIT(0); /* EN = 1 */ cdm->is_bypassed = false; + } else if ((cdm->out_intf == MDP_CDM_CDWN_OUTPUT_HDMI) && + ((data->csc_type == MDSS_MDP_CSC_RGB2RGB_L) || + (data->csc_type == MDSS_MDP_CSC_RGB2RGB))) { + op_mode &= ~BIT(2); /* DST_DATA_FORMAT = RGB */ + op_mode &= ~BIT(1); /* SRC_DATA_FORMAT = RGB */ + op_mode |= BIT(0); /* EN = 1 */ + cdm->is_bypassed = false; + cdm->is_only_clamped = true; } else { op_mode = 0; cdm->is_bypassed = true; @@ -299,8 +311,11 @@ static int mdss_mdp_cdm_out_packer_setup(struct mdss_mdp_cdm *cdm, } opmode &= ~0x6; opmode |= (fmt->chroma_sample << 1); - if (!cdm->is_bypassed) + if (!cdm->is_bypassed) { cdm_enable |= BIT(19); + if (cdm->is_only_clamped) + opmode = 0; + } } else { /* Disable HDMI pacler for WB */ diff --git a/drivers/video/fbdev/msm/mdss_mdp_cdm.h b/drivers/video/fbdev/msm/mdss_mdp_cdm.h index 3b7fdced6623..b2897d41044d 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_cdm.h +++ b/drivers/video/fbdev/msm/mdss_mdp_cdm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014,2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -60,6 +60,7 @@ struct mdss_mdp_cdm { bool is_bypassed; struct mdp_cdm_cfg setup; struct completion free_comp; + bool is_only_clamped; }; struct mdss_mdp_cdm *mdss_mdp_cdm_init(struct mdss_mdp_ctl *ctl, diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 1956b278e94a..96446511f32a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -103,6 +103,9 @@ struct mdss_mdp_video_ctx { u32 intf_irq_mask; spinlock_t mdss_mdp_video_lock; spinlock_t mdss_mdp_intf_intr_lock; + + enum mdss_mdp_csc_type cdm_csc_type; + bool yuv_conv; }; static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx, @@ -1023,6 +1026,8 @@ static int mdss_mdp_video_ctx_stop(struct mdss_mdp_ctl *ctl, mdss_mdp_set_intf_intr_callback(ctx, MDSS_MDP_INTF_IRQ_PROG_LINE, NULL, NULL); + ctx->yuv_conv = false; + ctx->ref_cnt--; end: mutex_unlock(&ctl->offlock); @@ -1645,6 +1650,75 @@ end: return rc; } +static int mdss_mdp_update_csc_matrix(struct mdss_mdp_ctl *ctl) +{ + struct mdss_mdp_video_ctx *ctx; + struct mdss_data_type *mdata; + struct mdss_panel_data *pdata; + struct mdss_panel_info *pinfo; + struct mdss_mdp_format_params *fmt; + enum mdss_mdp_csc_type csc_type; + int rc = 0; + + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; + if (!ctx) { + pr_err("%s: invalid ctx\n", __func__); + return -ENODEV; + } + + mdata = ctl->mdata; + pdata = ctl->panel_data; + pinfo = &pdata->panel_info; + + if (!mdss_mdp_is_cdm_supported(mdata, ctl->intf_type, 0)) { + pr_debug("%s: CDM is not supported\n", __func__); + goto error; + } + + if (IS_ERR_OR_NULL(ctl->cdm)) { + pr_debug("%s: CDM is not initialized\n", __func__); + goto error; + } + + if (!ctx->yuv_conv) { + pr_debug("%s: CDM not configured to convert to YUV yet\n", + __func__); + goto error; + } + + fmt = mdss_mdp_get_format_params(pinfo->out_format); + if (fmt->is_yuv) { + csc_type = MDSS_MDP_CSC_RGB2YUV_709L; + if (pdata->get_csc_type) + csc_type = pdata->get_csc_type(pdata); + + pr_debug("cdm_csc_type = %d csc_type = %d\n", + ctx->cdm_csc_type, csc_type); + if (ctx->cdm_csc_type != csc_type) { + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + rc = mdss_mdp_csc_setup(MDSS_MDP_BLOCK_CDM, + ctl->cdm->num, csc_type); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); + + if (rc) { + pr_err("%s: CDM CSC setup failed, rc = %d\n", + __func__, rc); + goto error; + } + + pr_debug("%s: updating csc %d to %d\n", __func__, + ctx->cdm_csc_type, csc_type); + + ctx->cdm_csc_type = csc_type; + pinfo->csc_type = csc_type; + ctl->flush_bits |= BIT(26); + } + } +error: + return rc; +} + static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) { struct mdss_mdp_video_ctx *ctx; @@ -1736,6 +1810,8 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) CTL_INTF_EVENT_FLAG_DEFAULT); } + rc = mdss_mdp_update_csc_matrix(ctl); + rc = mdss_mdp_video_avr_trigger_setup(ctl); if (rc) { pr_err("avr trigger setup failed\n"); @@ -1955,10 +2031,24 @@ static int mdss_mdp_video_cdm_setup(struct mdss_mdp_cdm *cdm, { struct mdp_cdm_cfg setup; - if (fmt->is_yuv) - setup.csc_type = MDSS_MDP_CSC_RGB2YUV_601FR; - else - setup.csc_type = MDSS_MDP_CSC_RGB2RGB; + if (fmt->is_yuv) { + if (pinfo->is_ce_mode) { + if (pinfo->yres < 720) + setup.csc_type = MDSS_MDP_CSC_RGB2YUV_601L; + else + setup.csc_type = MDSS_MDP_CSC_RGB2YUV_709L; + } else { + if (pinfo->yres < 720) + setup.csc_type = MDSS_MDP_CSC_RGB2YUV_601FR; + else + setup.csc_type = MDSS_MDP_CSC_RGB2YUV_709FR; + } + } else { + if (pinfo->is_ce_mode) + setup.csc_type = MDSS_MDP_CSC_RGB2RGB_L; + else + setup.csc_type = MDSS_MDP_CSC_RGB2RGB; + } switch (fmt->chroma_sample) { case MDSS_MDP_CHROMA_RGB: @@ -1984,8 +2074,10 @@ static int mdss_mdp_video_cdm_setup(struct mdss_mdp_cdm *cdm, return -EINVAL; } + pinfo->csc_type = setup.csc_type; + setup.out_format = pinfo->out_format; - setup.mdp_csc_bit_depth = MDP_CDM_CSC_8BIT; + setup.mdp_csc_bit_depth = MDP_CDM_CSC_10BIT; setup.output_width = pinfo->xres + pinfo->lcdc.xres_pad; setup.output_height = pinfo->yres + pinfo->lcdc.yres_pad; return mdss_mdp_cdm_setup(cdm, &setup); @@ -2120,6 +2212,7 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, } if (mdss_mdp_is_cdm_supported(mdata, ctl->intf_type, 0)) { + bool needs_qr_conversion = false; fmt = mdss_mdp_get_format_params(pinfo->out_format); if (!fmt) { @@ -2127,7 +2220,11 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, pinfo->out_format); return -EINVAL; } - if (fmt->is_yuv) { + + if (ctl->intf_type == MDSS_INTF_HDMI && pinfo->is_ce_mode) + needs_qr_conversion = true; + + if (fmt->is_yuv || needs_qr_conversion) { ctl->cdm = mdss_mdp_cdm_init(ctl, MDP_CDM_CDWN_OUTPUT_HDMI); if (!IS_ERR_OR_NULL(ctl->cdm)) { @@ -2137,6 +2234,10 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, __func__); return -EINVAL; } + if (fmt->is_yuv) + ctx->yuv_conv = true; + + ctx->cdm_csc_type = pinfo->csc_type; ctl->flush_bits |= BIT(26); } else { pr_err("%s: failed to initialize cdm\n", diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index 1ab39479d038..34591c84d5c3 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -120,6 +120,18 @@ struct mdp_csc_cfg mdp_csc_8bit_convert[MDSS_MDP_MAX_CSC] = { { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,}, { 0x0010, 0x00eb, 0x0010, 0x00f0, 0x0010, 0x00f0,}, }, + [MDSS_MDP_CSC_RGB2YUV_709FR] = { + 0, + { + 0x006d, 0x016e, 0x0025, + 0xffc5, 0xff3b, 0x0100, + 0x0100, 0xff17, 0xffe9 + }, + { 0x0, 0x0, 0x0,}, + { 0x0, 0x0080, 0x0080,}, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,}, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,}, + }, [MDSS_MDP_CSC_RGB2YUV_2020L] = { 0, { @@ -168,6 +180,18 @@ struct mdp_csc_cfg mdp_csc_8bit_convert[MDSS_MDP_MAX_CSC] = { { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,}, { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,}, }, + [MDSS_MDP_CSC_RGB2RGB_L] = { + 0, + { + 0x01b7, 0x0000, 0x0000, + 0x0000, 0x01b7, 0x0000, + 0x0000, 0x0000, 0x01b7, + }, + { 0x0, 0x0, 0x0,}, + { 0x10, 0x10, 0x10,}, + { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,}, + { 0x10, 0xeb, 0x10, 0xeb, 0x10, 0xeb,}, + }, }; struct mdp_csc_cfg mdp_csc_10bit_convert[MDSS_MDP_MAX_CSC] = { @@ -267,6 +291,18 @@ struct mdp_csc_cfg mdp_csc_10bit_convert[MDSS_MDP_MAX_CSC] = { { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, { 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,}, }, + [MDSS_MDP_CSC_RGB2YUV_709FR] = { + 0, + { + 0x006d, 0x016e, 0x0025, + 0xffc5, 0xff3b, 0x0100, + 0x0100, 0xff17, 0xffe9 + }, + { 0x0, 0x0, 0x0,}, + { 0x0, 0x0200, 0x0200,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + }, [MDSS_MDP_CSC_RGB2YUV_2020L] = { 0, { @@ -315,6 +351,18 @@ struct mdp_csc_cfg mdp_csc_10bit_convert[MDSS_MDP_MAX_CSC] = { { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, }, + [MDSS_MDP_CSC_RGB2RGB_L] = { + 0, + { + 0x01b7, 0x0000, 0x0000, + 0x0000, 0x01b7, 0x0000, + 0x0000, 0x0000, 0x01b7, + }, + { 0x0, 0x0, 0x0,}, + { 0x40, 0x40, 0x40,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x40, 0x3ac, 0x40, 0x3ac, 0x40, 0x3ac,}, + }, }; static struct mdss_mdp_format_params dest_scaler_fmt = { diff --git a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c index 734b7d77a8a7..b085bd21bee2 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c +++ b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -286,8 +286,9 @@ int mdss_mdp_splash_cleanup(struct msm_fb_data_type *mfd, /* 1-to-1 mapping */ mdss_mdp_splash_iommu_attach(mfd); - if (use_borderfill && mdp5_data->handoff && - !mfd->splash_info.iommu_dynamic_attached) { + if ((use_borderfill && mdp5_data->handoff && + !mfd->splash_info.iommu_dynamic_attached) || + mfd->force_null_commit) { /* * Set up border-fill on the handed off pipes. * This is needed to ensure that there are no memory diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index b6690a558ad9..28bf4d2c41ea 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -813,6 +813,8 @@ struct mdss_panel_info { u32 rst_seq_len; u32 vic; /* video identification code */ u32 deep_color; + bool is_ce_mode; /* CE video format */ + u8 csc_type; struct mdss_rect roi; struct mdss_dsi_dual_pu_roi dual_roi; int pwm_pmic_gpio; @@ -1003,6 +1005,7 @@ struct mdss_panel_data { * and teardown. */ int (*event_handler) (struct mdss_panel_data *pdata, int e, void *arg); + enum mdss_mdp_csc_type (*get_csc_type)(struct mdss_panel_data *pdata); struct device_node *(*get_fb_node)(struct platform_device *pdev); struct list_head timings_list; diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h index 40e5c273011f..b1c02f297e91 100644 --- a/include/uapi/media/msmb_isp.h +++ b/include/uapi/media/msmb_isp.h @@ -33,6 +33,8 @@ struct isp_kstate { uint32_t kernel_sofid; uint32_t drop_reconfig; uint32_t vfeid; + uint32_t dual_cam_drop_detected; + uint32_t dual_cam_drop; }; enum ISP_START_PIXEL_PATTERN { diff --git a/net/ipc_router/ipc_router_security.c b/net/ipc_router/ipc_router_security.c index 539c72f8158a..ab4e5003c05d 100644 --- a/net/ipc_router/ipc_router_security.c +++ b/net/ipc_router/ipc_router_security.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2014,2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -101,7 +101,7 @@ EXPORT_SYMBOL(check_permissions); int msm_ipc_config_sec_rules(void *arg) { struct config_sec_rules_args sec_rules_arg; - struct security_rule *rule, *temp_rule; + struct security_rule *rule; int key; size_t kgroup_info_sz; int ret; @@ -117,6 +117,10 @@ int msm_ipc_config_sec_rules(void *arg) if (ret) return -EFAULT; + /* Default rule change from config util not allowed */ + if (sec_rules_arg.service_id == ALL_SERVICE) + return -EINVAL; + if (sec_rules_arg.num_group_info <= 0) return -EINVAL; @@ -174,21 +178,11 @@ int msm_ipc_config_sec_rules(void *arg) key = rule->service_id & (SEC_RULES_HASH_SZ - 1); down_write(&security_rules_lock_lha4); - if (rule->service_id == ALL_SERVICE) { - temp_rule = list_first_entry(&security_rules[key], - struct security_rule, list); - list_del(&temp_rule->list); - kfree(temp_rule->group_id); - kfree(temp_rule); - } list_add_tail(&rule->list, &security_rules[key]); up_write(&security_rules_lock_lha4); - if (rule->service_id == ALL_SERVICE) - msm_ipc_sync_default_sec_rule((void *)rule); - else - msm_ipc_sync_sec_rule(rule->service_id, rule->instance_id, - (void *)rule); + msm_ipc_sync_sec_rule(rule->service_id, + rule->instance_id, (void *)rule); return 0; }