msm: mdss: add support CDM block in MDP driver

MDP has a new hardware block for converting mixer
output to yuv format before the data goes to the
interface block. This block will be used for enabling
yuv420 output on HDMI and writeback interfaces. In
case only one block is present, it needs to be
shared between HDMI and writeback.

Change-Id: I2688ca98c22d9f78e3c626ea8c1bf4c77713a3af
Signed-off-by: Dhaval Patel <pdhaval@codeaurora.org>
[cip@codeaurora.org: Moved new file locations]
Signed-off-by: Clarence Ip <cip@codeaurora.org>
This commit is contained in:
Dhaval Patel 2014-11-14 15:15:23 -08:00 committed by David Keitel
parent 72b4bbc9c9
commit 947a5d6ac5
11 changed files with 764 additions and 8 deletions

View file

@ -408,7 +408,13 @@ Fudge Factors: Fudge factors are used to boost demand for
qcom,mdss-pipe-dma-off
- qcom,mdss-has-dst-split: Boolean property to indicate if destination
split feature is available or not in the target.
- qcom,mdss-cdm-off: Array of offset addresses for the available
chroma down modules that can convert RGB data
to YUV before sending it to the interface
block. These offsets will be calculated from
register "mdp_phys" define in reg property. The
number of cdm offsets should reflect the number
of cdm blocks present in hardware.
Optional subnodes:
Child nodes representing the frame buffer virtual devices.
@ -599,6 +605,7 @@ Example:
0x00017100 0x00019100>;
qcom,mdss-intf-off = <0x00021100 0x00021300
0x00021500 0x00021700>;
qcom,mdss-cdm-off = <0x0007A200>;
/* buffer parameters to calculate prefill bandwidth */
qcom,mdss-prefill-outstanding-buffer-bytes = <1024>;

View file

@ -13,6 +13,7 @@ mdss-mdp-objs += mdss_mdp_rotator.o
mdss-mdp-objs += mdss_mdp_overlay.o
mdss-mdp-objs += mdss_mdp_splash_logo.o
mdss-mdp-objs += mdss_mdp_wb.o
mdss-mdp-objs += mdss_mdp_cdm.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_mdp_debug.o

View file

@ -268,6 +268,10 @@ struct mdss_data_type {
u64 ab[MDSS_MAX_BUS_CLIENTS];
u64 ib[MDSS_MAX_BUS_CLIENTS];
struct mdss_pp_block_off pp_block_off;
struct mdss_mdp_cdm *cdm_off;
u32 ncdm;
struct mutex cdm_lock;
};
extern struct mdss_data_type *mdss_res;

View file

@ -170,6 +170,7 @@ static int mdss_mdp_parse_dt_prefill(struct platform_device *pdev);
static int mdss_mdp_parse_dt_misc(struct platform_device *pdev);
static int mdss_mdp_parse_dt_ad_cfg(struct platform_device *pdev);
static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev);
static int mdss_mdp_parse_dt_cdm(struct platform_device *pdev);
/**
* mdss_mdp_vbif_axi_halt() - Halt MDSS AXI ports
@ -1723,6 +1724,10 @@ static int mdss_mdp_parse_dt(struct platform_device *pdev)
return rc;
}
rc = mdss_mdp_parse_dt_cdm(pdev);
if (rc)
pr_debug("CDM offset not found in device tree\n");
/* Parse the mdp specific register base offset*/
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,mdss-mdp-reg-offset", &data);
@ -2253,6 +2258,75 @@ dspp_alloc_fail:
return rc;
}
static int mdss_mdp_cdm_addr_setup(struct mdss_data_type *mdata,
u32 *cdm_offsets, u32 len)
{
struct mdss_mdp_cdm *head;
u32 i = 0;
head = devm_kzalloc(&mdata->pdev->dev, sizeof(struct mdss_mdp_cdm) *
len, GFP_KERNEL);
if (!head) {
pr_err("%s: no memory for CDM info\n", __func__);
return -ENOMEM;
}
for (i = 0; i < len; i++) {
head[i].num = i;
head[i].base = (mdata->mdss_io.base) + cdm_offsets[i];
atomic_set(&head[i].kref.refcount, 0);
mutex_init(&head[i].lock);
pr_debug("%s: cdm off (%d) = %p\n", __func__, i, head[i].base);
}
mdata->cdm_off = head;
mutex_init(&mdata->cdm_lock);
return 0;
}
static int mdss_mdp_parse_dt_cdm(struct platform_device *pdev)
{
int rc = 0;
u32 *cdm_offsets = NULL;
struct mdss_data_type *mdata = platform_get_drvdata(pdev);
mdata->ncdm = mdss_mdp_parse_dt_prop_len(pdev, "qcom,mdss-cdm-off");
if (!mdata->ncdm) {
rc = 0;
pr_debug("%s: No CDM offsets present in DT\n", __func__);
goto end;
}
pr_debug("%s: cdm len == %d\n", __func__, mdata->ncdm);
cdm_offsets = kzalloc(sizeof(u32) * mdata->ncdm, GFP_KERNEL);
if (!cdm_offsets) {
pr_err("no more memory for cdm offsets\n");
rc = -ENOMEM;
mdata->ncdm = 0;
goto end;
}
rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-cdm-off", cdm_offsets,
mdata->ncdm);
if (rc) {
pr_err("device tree err: failed to get cdm offsets\n");
goto fail;
}
rc = mdss_mdp_cdm_addr_setup(mdata, cdm_offsets, mdata->ncdm);
if (rc) {
pr_err("%s: CDM address setup failed\n", __func__);
goto fail;
}
fail:
kfree(cdm_offsets);
if (rc)
mdata->ncdm = 0;
end:
return rc;
}
static int mdss_mdp_parse_dt_ctl(struct platform_device *pdev)
{
int rc = 0;

View file

@ -25,6 +25,7 @@
#include "mdss.h"
#include "mdss_mdp_hwio.h"
#include "mdss_fb.h"
#include "mdss_mdp_cdm.h"
#define MDSS_MDP_DEFAULT_INTR_MASK 0
#define MDSS_MDP_CURSOR_WIDTH 64
@ -107,6 +108,7 @@ enum mdss_mdp_block_type {
MDSS_MDP_BLOCK_MIXER,
MDSS_MDP_BLOCK_DSPP,
MDSS_MDP_BLOCK_WB,
MDSS_MDP_BLOCK_CDM,
MDSS_MDP_BLOCK_MAX
};
@ -224,6 +226,7 @@ struct mdss_mdp_ctl {
struct msm_fb_data_type *mfd;
struct mdss_mdp_mixer *mixer_left;
struct mdss_mdp_mixer *mixer_right;
struct mdss_mdp_cdm *cdm;
struct mutex lock;
struct mutex *shared_lock;
spinlock_t spin_lock;
@ -739,6 +742,21 @@ static inline int mdss_mdp_is_ubwc_supported(struct mdss_data_type *mdata)
return IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_107);
}
static inline int mdss_mdp_is_cdm_supported(struct mdss_data_type *mdata,
u32 intf_type, u32 mixer_type)
{
int support = mdata->ncdm;
/*
* CDM is supported under these conditions
* 1. If Device tree created a cdm block AND
* 2. Output interface is HDMI OR Output interface is WB2
*/
return support && ((intf_type == MDSS_INTF_HDMI) ||
((intf_type == MDSS_MDP_NO_INTF) &&
(mixer_type == MDSS_MDP_MIXER_TYPE_INTF)));
}
irqreturn_t mdss_mdp_isr(int irq, void *ptr);
int mdss_iommu_attach(struct mdss_data_type *mdata);
int mdss_iommu_dettach(struct mdss_data_type *mdata);

View file

@ -0,0 +1,386 @@
/* Copyright (c) 2014, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/errno.h>
#include <linux/mutex.h>
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_mdp_trace.h"
#include "mdss_debug.h"
static u32 cdm_cdwn2_cosite_h_coeff[] = {0x00000016, 0x000001cc, 0x0100009e};
static u32 cdm_cdwn2_offsite_h_coeff[] = {0x000b0005, 0x01db01eb, 0x00e40046};
static u32 cdm_cdwn2_cosite_v_coeff[] = {0x00080004};
static u32 cdm_cdwn2_offsite_v_coeff[] = {0x00060002};
/* Limited Range rgb2yuv coeff with clamp and bias values for CSC 10 module */
static struct mdp_csc_cfg cdm_rgb2yuv_coeff = {
0,
{
0x0083, 0x0102, 0x0032,
0x1fb5, 0x1f6c, 0x00e1,
0x00e1, 0x1f45, 0x1fdc
},
{ 0x00, 0x00, 0x00 },
{ 0x0040, 0x0200, 0x0200 },
{ 0x000, 0x3ff, 0x000, 0x3ff, 0x000, 0x3ff },
{ 0x040, 0x3ac, 0x040, 0x3c0, 0x040, 0x3c0 },
};
/**
* @mdss_mdp_cdm_alloc() - Allocates a cdm block by parsing the list of
* available cdm blocks.
*
* @mdata - structure containing the list of cdm blocks
*/
static struct mdss_mdp_cdm *mdss_mdp_cdm_alloc(struct mdss_data_type *mdata)
{
struct mdss_mdp_cdm *cdm = NULL;
u32 i = 0;
mutex_lock(&mdata->cdm_lock);
for (i = 0; i < mdata->ncdm; i++) {
cdm = mdata->cdm_off + i;
if (atomic_read(&cdm->kref.refcount) == 0) {
kref_init(&cdm->kref);
cdm->mdata = mdata;
pr_debug("alloc cdm=%d\n", cdm->num);
break;
}
cdm = NULL;
}
mutex_unlock(&mdata->cdm_lock);
return cdm;
}
/**
* @mdss_mdp_cdm_free() - Adds the CDM block back to the available list
* @kref: Reference count structure
*/
static void mdss_mdp_cdm_free(struct kref *kref)
{
struct mdss_mdp_cdm *cdm = container_of(kref, struct mdss_mdp_cdm,
kref);
if (!cdm)
return;
pr_debug("free cdm_num = %d\n", cdm->num);
}
/**
* @mdss_mdp_cdm_init() - Allocates a CDM block and initializes the hardware
* and software context. This should be called once at
* when setting up the usecase and released when done.
* @ctl: Pointer to the control structure.
* @intf_type: Output interface which will be connected to CDM.
*/
struct mdss_mdp_cdm *mdss_mdp_cdm_init(struct mdss_mdp_ctl *ctl, u32 intf_type)
{
struct mdss_data_type *mdata = ctl->mdata;
struct mdss_mdp_cdm *cdm = NULL;
cdm = mdss_mdp_cdm_alloc(mdata);
if (!cdm) {
pr_err("%s: Unable to allocate cdm\n", __func__);
return ERR_PTR(-EBUSY);
}
cdm->out_intf = intf_type;
cdm->is_bypassed = true;
memset(&cdm->setup, 0x0, sizeof(struct mdp_cdm_cfg));
/*
* Setup RGB to YUV conversion in CDM. CDM will be in bypass mode for
* other cases
*/
mdss_mdp_csc_setup_data(MDSS_MDP_BLOCK_CDM, cdm->num,
&cdm_rgb2yuv_coeff);
return cdm;
}
/**
* @mdss_mdp_cdm_csc_setup - Programs the CSC block.
* @cdm: Pointer to the CDM structure.
* @data: Pointer to the structure containing configuration
* data.
*/
static int mdss_mdp_cdm_csc_setup(struct mdss_mdp_cdm *cdm,
struct mdp_cdm_cfg *data)
{
int rc = 0;
u32 op_mode = 0;
if (data->csc_type == MDSS_MDP_CSC_RGB2YUV) {
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 {
op_mode = 0;
cdm->is_bypassed = true;
}
writel_relaxed(op_mode, cdm->base + MDSS_MDP_REG_CDM_CSC_10_OPMODE);
return rc;
}
/**
* @mdss_mdp_cdm_cdwn_setup - Programs the chroma down block.
* @cdm: Pointer to the CDM structure.
* @data: Pointer to the structure containing configuration
* data.
*/
static int mdss_mdp_cdm_cdwn_setup(struct mdss_mdp_cdm *cdm,
struct mdp_cdm_cfg *data)
{
int rc = 0;
u32 opmode = 0;
u32 out_size = 0;
if (data->mdp_csc_bit_depth == MDP_CDM_CSC_10BIT)
opmode &= ~BIT(7);
else
opmode |= BIT(7);
/* ENABLE DWNS_H bit */
opmode |= BIT(1);
switch (data->horz_downsampling_type) {
case MDP_CDM_CDWN_DISABLE:
/* CLEAR METHOD_H field */
opmode &= ~(0x18);
/* CLEAR DWNS_H bit */
opmode &= ~BIT(1);
break;
case MDP_CDM_CDWN_PIXEL_DROP:
/* Clear METHOD_H field (pixel drop is 0) */
opmode &= ~(0x18);
break;
case MDP_CDM_CDWN_AVG:
/* Clear METHOD_H field (Average is 0x1) */
opmode &= ~(0x18);
opmode |= (0x1 << 0x3);
break;
case MDP_CDM_CDWN_COSITE:
/* Clear METHOD_H field (Average is 0x2) */
opmode &= ~(0x18);
opmode |= (0x2 << 0x3);
/* Co-site horizontal coefficients */
writel_relaxed(cdm_cdwn2_cosite_h_coeff[0], cdm->base +
MDSS_MDP_REG_CDM_CDWN2_COEFF_COSITE_H_0);
writel_relaxed(cdm_cdwn2_cosite_h_coeff[1], cdm->base +
MDSS_MDP_REG_CDM_CDWN2_COEFF_COSITE_H_1);
writel_relaxed(cdm_cdwn2_cosite_h_coeff[2], cdm->base +
MDSS_MDP_REG_CDM_CDWN2_COEFF_COSITE_H_2);
break;
case MDP_CDM_CDWN_OFFSITE:
/* Clear METHOD_H field (Average is 0x3) */
opmode &= ~(0x18);
opmode |= (0x3 << 0x3);
/* Off-site horizontal coefficients */
writel_relaxed(cdm_cdwn2_offsite_h_coeff[0], cdm->base +
MDSS_MDP_REG_CDM_CDWN2_COEFF_OFFSITE_H_0);
writel_relaxed(cdm_cdwn2_offsite_h_coeff[1], cdm->base +
MDSS_MDP_REG_CDM_CDWN2_COEFF_OFFSITE_H_1);
writel_relaxed(cdm_cdwn2_offsite_h_coeff[2], cdm->base +
MDSS_MDP_REG_CDM_CDWN2_COEFF_OFFSITE_H_2);
break;
default:
pr_err("%s invalid horz down sampling type\n", __func__);
return -EINVAL;
}
/* ENABLE DWNS_V bit */
opmode |= BIT(2);
switch (data->vert_downsampling_type) {
case MDP_CDM_CDWN_DISABLE:
/* CLEAR METHOD_V field */
opmode &= ~(0x60);
/* CLEAR DWNS_V bit */
opmode &= ~BIT(2);
break;
case MDP_CDM_CDWN_PIXEL_DROP:
/* Clear METHOD_V field (pixel drop is 0) */
opmode &= ~(0x60);
break;
case MDP_CDM_CDWN_AVG:
/* Clear METHOD_V field (Average is 0x1) */
opmode &= ~(0x60);
opmode |= (0x1 << 0x5);
break;
case MDP_CDM_CDWN_COSITE:
/* Clear METHOD_V field (Average is 0x2) */
opmode &= ~(0x60);
opmode |= (0x2 << 0x5);
/* Co-site vertical coefficients */
writel_relaxed(cdm_cdwn2_cosite_v_coeff[0], cdm->base +
MDSS_MDP_REG_CDM_CDWN2_COEFF_COSITE_V);
break;
case MDP_CDM_CDWN_OFFSITE:
/* Clear METHOD_V field (Average is 0x3) */
opmode &= ~(0x60);
opmode |= (0x3 << 0x5);
/* Off-site vertical coefficients */
writel_relaxed(cdm_cdwn2_offsite_v_coeff[0], cdm->base +
MDSS_MDP_REG_CDM_CDWN2_COEFF_OFFSITE_V);
break;
default:
pr_err("%s invalid vert down sampling type\n", __func__);
return -EINVAL;
}
if (data->vert_downsampling_type || data->horz_downsampling_type)
opmode |= BIT(0); /* EN CDWN module */
else
opmode &= ~BIT(0);
out_size = (data->output_width & 0xFFFF) |
((data->output_height & 0xFFFF) << 16);
writel_relaxed(out_size, cdm->base + MDSS_MDP_REG_CDM_CDWN2_OUT_SIZE);
writel_relaxed(opmode, cdm->base + MDSS_MDP_REG_CDM_CDWN2_OP_MODE);
writel_relaxed(((0x3FF << 16) | 0x0),
cdm->base + MDSS_MDP_REG_CDM_CDWN2_CLAMP_OUT);
return rc;
}
/**
* @mdss_mdp_cdm_out_packer_setup - Programs the output packer block.
* @cdm: Pointer to the CDM structure.
* @data: Pointer to the structure containing
* configuration data.
*/
static int mdss_mdp_cdm_out_packer_setup(struct mdss_mdp_cdm *cdm,
struct mdp_cdm_cfg *data)
{
int rc = 0;
u32 opmode = 0;
u32 cdm_enable = 0;
struct mdss_mdp_format_params *fmt;
if (cdm->out_intf == MDP_CDM_CDWN_OUTPUT_HDMI) {
/* Enable HDMI packer */
opmode |= BIT(0);
fmt = mdss_mdp_get_format_params(data->out_format);
if (!fmt) {
pr_err("cdm format = %d, not supported\n",
data->out_format);
return -EINVAL;
}
opmode &= ~0x6;
opmode |= (fmt->chroma_sample << 1);
if (!cdm->is_bypassed)
cdm_enable |= BIT(19);
} else {
/* Disable HDMI pacler for WB */
opmode = 0;
if (!cdm->is_bypassed)
cdm_enable |= BIT(24);
}
writel_relaxed(cdm_enable, cdm->mdata->mdp_base +
MDSS_MDP_MDP_OUT_CTL_0);
writel_relaxed(opmode, cdm->base + MDSS_MDP_REG_CDM_HDMI_PACK_OP_MODE);
return rc;
}
/**
* @mdss_mdp_cdm_setup - Sets up the CDM block based on the usecase. The CDM
* block should be initialized before calling this
* function.
* @cdm: Pointer to the CDM structure.
* @data: Pointer to the structure containing configuration
* data.
*/
int mdss_mdp_cdm_setup(struct mdss_mdp_cdm *cdm, struct mdp_cdm_cfg *data)
{
int rc = 0;
if (!cdm || !data) {
pr_err("%s: invalid arguments\n", __func__);
return -EINVAL;
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
mutex_lock(&cdm->lock);
/* Setup CSC block */
rc = mdss_mdp_cdm_csc_setup(cdm, data);
if (rc) {
pr_err("%s: csc configuration failure\n", __func__);
goto fail;
}
/* Setup chroma down sampler */
rc = mdss_mdp_cdm_cdwn_setup(cdm, data);
if (rc) {
pr_err("%s: cdwn configuration failure\n", __func__);
goto fail;
}
/* Setup HDMI packer */
rc = mdss_mdp_cdm_out_packer_setup(cdm, data);
if (rc) {
pr_err("%s: out packer configuration failure\n", __func__);
goto fail;
}
memcpy(&cdm->setup, data, sizeof(struct mdp_cdm_cfg));
fail:
mutex_unlock(&cdm->lock);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
return rc;
}
/**
* @mdss_mdp_cdm_destroy - Destroys the CDM configuration and return it to
* default state.
* @cdm: Pointer to the CDM structure
*/
int mdss_mdp_cdm_destroy(struct mdss_mdp_cdm *cdm)
{
int rc = 0;
if (!cdm) {
pr_err("%s: invalid parameters\n", __func__);
return -EINVAL;
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
mutex_lock(&cdm->lock);
/* Disable HDMI packer */
writel_relaxed(0x0, cdm->base + MDSS_MDP_REG_CDM_HDMI_PACK_OP_MODE);
/* Put CDM in bypass */
writel_relaxed(0x0, cdm->mdata->mdp_base + MDSS_MDP_MDP_OUT_CTL_0);
mutex_unlock(&cdm->lock);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
kref_put(&cdm->kref, mdss_mdp_cdm_free);
return rc;
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2014, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef MDSS_MDP_CDM_H
#define MDSS_MDP_CDM_H
#include <linux/msm_mdp.h>
#include <linux/kref.h>
enum mdp_cdm_cdwn_method_type {
MDP_CDM_CDWN_DISABLE,
MDP_CDM_CDWN_PIXEL_DROP,
MDP_CDM_CDWN_AVG,
MDP_CDM_CDWN_COSITE,
MDP_CDM_CDWN_OFFSITE,
};
enum mdp_cdm_cdwn_output_type {
MDP_CDM_CDWN_OUTPUT_HDMI,
MDP_CDM_CDWN_OUTPUT_WB,
};
enum mdp_cdm_csc_bit_depth {
MDP_CDM_CSC_8BIT,
MDP_CDM_CSC_10BIT,
};
struct mdp_cdm_cfg {
/* CSC block configuration */
u32 mdp_csc_bit_depth;
u32 csc_type;
/* CDWN block configuration */
u32 horz_downsampling_type;
u32 vert_downsampling_type;
/* Output packer configuration */
u32 output_width;
u32 output_height;
u32 out_format;
};
struct mdss_mdp_cdm {
u32 num;
char __iomem *base;
struct kref kref;
struct mutex lock;
struct mdss_data_type *mdata;
u32 out_intf;
bool is_bypassed;
struct mdp_cdm_cfg setup;
};
struct mdss_mdp_cdm *mdss_mdp_cdm_init(struct mdss_mdp_ctl *ctl,
u32 intf_type);
int mdss_mdp_cdm_destroy(struct mdss_mdp_cdm *cdm);
int mdss_mdp_cdm_setup(struct mdss_mdp_cdm *cdm, struct mdp_cdm_cfg *data);
#endif /* MDSS_MDP_CDM_H */

View file

@ -623,6 +623,27 @@ enum mdss_mdp_pingpong_index {
#define MDSS_MDP_INTF_MISR_CTRL 0x180
#define MDSS_MDP_INTF_MISR_SIGNATURE (MDSS_MDP_INTF_MISR_CTRL + 0x4)
#define MDSS_MDP_REG_CDM_CSC_10_OPMODE 0x000
#define MDSS_MDP_REG_CDM_CSC_10_BASE 0x004
#define MDSS_MDP_REG_CDM_CDWN2_OP_MODE 0x100
#define MDSS_MDP_REG_CDM_CDWN2_CLAMP_OUT 0x104
#define MDSS_MDP_REG_CDM_CDWN2_PARAMS_3D_0 0x108
#define MDSS_MDP_REG_CDM_CDWN2_PARAMS_3D_1 0x10C
#define MDSS_MDP_REG_CDM_CDWN2_COEFF_COSITE_H_0 0x110
#define MDSS_MDP_REG_CDM_CDWN2_COEFF_COSITE_H_1 0x114
#define MDSS_MDP_REG_CDM_CDWN2_COEFF_COSITE_H_2 0x118
#define MDSS_MDP_REG_CDM_CDWN2_COEFF_OFFSITE_H_0 0x11C
#define MDSS_MDP_REG_CDM_CDWN2_COEFF_OFFSITE_H_1 0x120
#define MDSS_MDP_REG_CDM_CDWN2_COEFF_OFFSITE_H_2 0x124
#define MDSS_MDP_REG_CDM_CDWN2_COEFF_COSITE_V 0x128
#define MDSS_MDP_REG_CDM_CDWN2_COEFF_OFFSITE_V 0x12C
#define MDSS_MDP_REG_CDM_CDWN2_OUT_SIZE 0x130
#define MDSS_MDP_REG_CDM_HDMI_PACK_OP_MODE 0x200
/* Following offsets are with respect to MDP base */
#define MDSS_MDP_MDP_OUT_CTL_0 0x410
/* following offsets are with respect to MDP VBIF base */
#define MMSS_VBIF_XIN_HALT_CTRL0 0x200
#define MMSS_VBIF_XIN_HALT_CTRL1 0x204

View file

@ -309,6 +309,17 @@ static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl,
mdp_video_write(ctx, MDSS_MDP_REG_INTF_POLARITY_CTL, polarity_ctl);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN, 0x3);
/*
* If CDM is present Interface should have destination
* format set to RGB
*/
if (ctl->cdm) {
u32 reg = mdp_video_read(ctx, MDSS_MDP_REG_INTF_CONFIG);
reg &= ~BIT(18); /* CSC_DST_DATA_FORMAT = RGB */
reg &= ~BIT(17); /* CSC_SRC_DATA_FROMAT = RGB */
mdp_video_write(ctx, MDSS_MDP_REG_INTF_CONFIG, reg);
}
return 0;
}
@ -499,6 +510,10 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
mdss_mdp_ctl_reset(ctl);
ctl->priv_data = NULL;
if (ctl->cdm) {
mdss_mdp_cdm_destroy(ctl->cdm);
ctl->cdm = NULL;
}
return 0;
}
@ -1092,6 +1107,49 @@ static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx,
mdp_video_write(ctx, MDSS_MDP_REG_INTF_CONFIG, fetch_enable);
}
static int mdss_mdp_video_cdm_setup(struct mdss_mdp_cdm *cdm,
struct mdss_panel_info *pinfo)
{
struct mdss_mdp_format_params *fmt;
struct mdp_cdm_cfg setup;
fmt = mdss_mdp_get_format_params(pinfo->out_format);
if (!fmt) {
pr_err("%s: format %d not supported\n", __func__,
pinfo->out_format);
return -EINVAL;
}
setup.out_format = pinfo->out_format;
if (fmt->is_yuv)
setup.csc_type = MDSS_MDP_CSC_RGB2YUV;
else
setup.csc_type = MDSS_MDP_CSC_RGB2RGB;
switch (fmt->chroma_sample) {
case MDSS_MDP_CHROMA_RGB:
setup.horz_downsampling_type = MDP_CDM_CDWN_DISABLE;
setup.vert_downsampling_type = MDP_CDM_CDWN_DISABLE;
break;
case MDSS_MDP_CHROMA_H2V1:
setup.horz_downsampling_type = MDP_CDM_CDWN_COSITE;
setup.vert_downsampling_type = MDP_CDM_CDWN_DISABLE;
break;
case MDSS_MDP_CHROMA_420:
setup.horz_downsampling_type = MDP_CDM_CDWN_COSITE;
setup.vert_downsampling_type = MDP_CDM_CDWN_OFFSITE;
break;
case MDSS_MDP_CHROMA_H1V2:
default:
pr_err("%s: unsupported chroma sampling type\n", __func__);
return -EINVAL;
}
setup.mdp_csc_bit_depth = MDP_CDM_CSC_8BIT;
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);
}
static int mdss_mdp_video_intfs_setup(struct mdss_mdp_ctl *ctl,
struct mdss_panel_data *pdata, int inum)
{
@ -1151,6 +1209,23 @@ static int mdss_mdp_video_intfs_setup(struct mdss_mdp_ctl *ctl,
ctx->intf_recovery.data = NULL;
}
if (mdss_mdp_is_cdm_supported(mdata, ctl->intf_type, 0)) {
ctl->cdm = mdss_mdp_cdm_init(ctl, MDP_CDM_CDWN_OUTPUT_HDMI);
if (ctl->cdm) {
if (mdss_mdp_video_cdm_setup(ctl->cdm, pinfo)) {
pr_err("%s: setting up cdm failed\n",
__func__);
return -EINVAL;
}
ctl->flush_bits |= BIT(26);
} else {
pr_err("%s: failed to initialize cdm\n", __func__);
return -EINVAL;
}
} else {
pr_debug("%s: cdm not supported\n", __func__);
}
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC,
(inum + MDSS_MDP_INTF0),
mdss_mdp_video_vsync_intr_done, ctl);
@ -1175,7 +1250,18 @@ static int mdss_mdp_video_intfs_setup(struct mdss_mdp_ctl *ctl,
itp.v_front_porch = pinfo->lcdc.v_front_porch;
itp.hsync_pulse_width = pinfo->lcdc.h_pulse_width;
itp.vsync_pulse_width = pinfo->lcdc.v_pulse_width;
/*
* In case of YUV420 output, MDP outputs data at half the rate. So
* reduce all horizontal parameters by half
*/
if (ctl->cdm && pinfo->out_format == MDP_Y_CBCR_H2V2) {
itp.width >>= 1;
itp.hsync_skew >>= 1;
itp.xres >>= 1;
itp.h_back_porch >>= 1;
itp.h_front_porch >>= 1;
itp.hsync_pulse_width >>= 1;
}
if (!ctl->panel_data->panel_info.cont_splash_enabled) {
if (mdss_mdp_video_timegen_setup(ctl, &itp)) {
pr_err("unable to set timing parameters intfs: %d\n",

View file

@ -138,8 +138,51 @@ static int mdss_mdp_writeback_addr_setup(struct mdss_mdp_writeback_ctx *ctx,
return 0;
}
static int mdss_mdp_writeback_cdm_setup(struct mdss_mdp_writeback_ctx *ctx,
struct mdss_mdp_cdm *cdm, u32 format)
{
struct mdss_mdp_format_params *fmt;
struct mdp_cdm_cfg setup;
fmt = mdss_mdp_get_format_params(format);
if (!fmt) {
pr_err("%s: format %d not supported\n", __func__, format);
return -EINVAL;
}
if (fmt->is_yuv)
setup.csc_type = MDSS_MDP_CSC_RGB2YUV;
else
setup.csc_type = MDSS_MDP_CSC_RGB2RGB;
switch (fmt->chroma_sample) {
case MDSS_MDP_CHROMA_RGB:
setup.horz_downsampling_type = MDP_CDM_CDWN_DISABLE;
setup.vert_downsampling_type = MDP_CDM_CDWN_DISABLE;
break;
case MDSS_MDP_CHROMA_H2V1:
setup.horz_downsampling_type = MDP_CDM_CDWN_COSITE;
setup.vert_downsampling_type = MDP_CDM_CDWN_DISABLE;
break;
case MDSS_MDP_CHROMA_420:
setup.horz_downsampling_type = MDP_CDM_CDWN_COSITE;
setup.vert_downsampling_type = MDP_CDM_CDWN_OFFSITE;
break;
case MDSS_MDP_CHROMA_H1V2:
default:
pr_err("%s: unsupported chroma sampling type\n", __func__);
return -EINVAL;
}
setup.out_format = format;
setup.mdp_csc_bit_depth = MDP_CDM_CSC_8BIT;
setup.output_width = ctx->width;
setup.output_height = ctx->height;
return mdss_mdp_cdm_setup(cdm, &setup);
}
static int mdss_mdp_writeback_format_setup(struct mdss_mdp_writeback_ctx *ctx,
u32 format)
u32 format, struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_format_params *fmt;
u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
@ -147,6 +190,7 @@ static int mdss_mdp_writeback_format_setup(struct mdss_mdp_writeback_ctx *ctx,
u32 opmode = ctx->opmode;
bool rotation = false;
struct mdss_data_type *mdata;
int rc;
pr_debug("wb_num=%d format=%d\n", ctx->wb_num, format);
@ -167,7 +211,18 @@ static int mdss_mdp_writeback_format_setup(struct mdss_mdp_writeback_ctx *ctx,
chroma_samp = fmt->chroma_sample;
if (ctx->type != MDSS_MDP_WRITEBACK_TYPE_ROTATOR && fmt->is_yuv) {
if (ctl->cdm) {
rc = mdss_mdp_writeback_cdm_setup(ctx, ctl->cdm, format);
if (rc) {
pr_err("%s: CDM configuration failed with error %d\n",
__func__, rc);
return rc;
}
ctl->flush_bits |= BIT(26);
}
if (ctx->type != MDSS_MDP_WRITEBACK_TYPE_ROTATOR &&
fmt->is_yuv && !ctl->cdm) {
mdss_mdp_csc_setup(MDSS_MDP_BLOCK_WB, ctx->wb_num,
MDSS_MDP_CSC_RGB2YUV);
opmode |= (1 << 8) | /* CSC_EN */
@ -233,7 +288,8 @@ static int mdss_mdp_writeback_format_setup(struct mdss_mdp_writeback_ctx *ctx,
outsize = (ctx->dst_rect.h << 16) | ctx->dst_rect.w;
if (mdss_mdp_is_ubwc_format(fmt)) {
opmode |= BIT(0);
if (!ctl->cdm)
opmode |= BIT(0);
dst_format |= BIT(31);
}
@ -245,7 +301,8 @@ static int mdss_mdp_writeback_format_setup(struct mdss_mdp_writeback_ctx *ctx,
}
mdp_wb_write(ctx, MDSS_MDP_REG_WB_ALPHA_X_VALUE, 0xFF);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_FORMAT, dst_format);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_OP_MODE, opmode);
if (!ctl->cdm)
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_OP_MODE, opmode);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_PACK_PATTERN, pattern);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_YSTRIDE0, ystride0);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_YSTRIDE1, ystride1);
@ -276,7 +333,7 @@ static int mdss_mdp_writeback_prepare_wfd(struct mdss_mdp_ctl *ctl, void *arg)
ctx->dst_rect.w = ctx->width;
ctx->dst_rect.h = ctx->height;
ret = mdss_mdp_writeback_format_setup(ctx, ctl->dst_format);
ret = mdss_mdp_writeback_format_setup(ctx, ctl->dst_format, ctl);
if (ret) {
pr_err("format setup failed\n");
return ret;
@ -348,7 +405,7 @@ static int mdss_mdp_writeback_prepare_rot(struct mdss_mdp_ctl *ctl, void *arg)
if (ctx->rot90)
ctx->opmode |= BIT(5); /* ROT 90 */
return mdss_mdp_writeback_format_setup(ctx, format);
return mdss_mdp_writeback_format_setup(ctx, format, ctl);
}
static int mdss_mdp_wb_add_vsync_handler(struct mdss_mdp_ctl *ctl,
@ -428,6 +485,10 @@ static int mdss_mdp_writeback_stop(struct mdss_mdp_ctl *ctl,
ctx->ref_cnt--;
}
if (ctl->cdm) {
mdss_mdp_cdm_destroy(ctl->cdm);
ctl->cdm = NULL;
}
return 0;
}
@ -686,6 +747,7 @@ int mdss_mdp_writeback_start(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_writeback_ctx *ctx;
u32 mem_sel;
u32 mixer_type = 0;
pr_debug("start ctl=%d\n", ctl->num);
@ -701,6 +763,21 @@ int mdss_mdp_writeback_start(struct mdss_mdp_ctl *ctl)
pr_err("invalid writeback mode %d\n", mem_sel);
return -EINVAL;
}
if (ctl->mixer_left)
mixer_type = ctl->mixer_left->type;
else
mixer_type = MDSS_MDP_MIXER_TYPE_UNUSED;
if (mdss_mdp_is_cdm_supported(ctl->mdata, ctl->intf_type,
ctl->mixer_left->type)) {
ctl->cdm = mdss_mdp_cdm_init(ctl, MDP_CDM_CDWN_OUTPUT_WB);
if (!ctl->cdm) {
pr_err("%s failed to init cdm\n", __func__);
return -EBUSY;
}
} else {
pr_debug("%s: cdm not supported\n", __func__);
}
ctl->priv_data = ctx;
ctx->wb_num = ctl->num; /* wb num should match ctl num */
ctx->base = ctl->wb_base;

View file

@ -410,6 +410,7 @@ int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, struct mdp_csc_cfg *data)
struct mdss_data_type *mdata;
struct mdss_mdp_pipe *pipe;
struct mdss_mdp_ctl *ctl;
struct mdss_mdp_cdm *cdm;
if (data == NULL) {
@ -445,6 +446,18 @@ int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, struct mdp_csc_cfg *data)
ret = -EINVAL;
}
break;
case MDSS_MDP_BLOCK_CDM:
if (blk_idx < mdata->ncdm) {
cdm = mdata->cdm_off + blk_idx;
if (cdm->base)
base = cdm->base +
MDSS_MDP_REG_CDM_CSC_10_BASE;
else
ret = -EINVAL;
} else {
ret = -EINVAL;
}
break;
default:
ret = -EINVAL;
break;