368 lines
9.7 KiB
C
368 lines
9.7 KiB
C
|
/* Copyright (c) 2014-2015, 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};
|
||
|
|
||
|
/**
|
||
|
* @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));
|
||
|
|
||
|
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;
|
||
|
|
||
|
mdss_mdp_csc_setup(MDSS_MDP_BLOCK_CDM, cdm->num, data->csc_type);
|
||
|
|
||
|
if (data->csc_type == MDSS_MDP_CSC_RGB2YUV_601L) {
|
||
|
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;
|
||
|
}
|