1
0
Fork 0

MLK-11431-1: IPU: forward IPU display drivers to 4.1.y kernel

Forward imx_3.14.y IPU and display drivers to 4.1 kernel.
This includes IPU core driver, display driver, LDB and HDMI driver.

Signed-off-by: Sandor Yu <R01008@freescale.com>
pull/10/head
Sandor Yu 2015-08-26 16:42:02 +08:00 committed by Jason Liu
parent 312f2f4787
commit 4dc795af9e
46 changed files with 29136 additions and 23 deletions

View File

@ -0,0 +1,105 @@
* FSL IPUv3 Display/FB
The FSL IPUv3 is Image Processing Unit version 3, a part of video and graphics
subsystem in an application processor. The goal of the IPU is to provide
comprehensive support for the flow of data from an image sensor or/and to a
display device.
Two IPU units are on the imx6q SOC while only one IPU unit on the imx6dl SOC.
Each IPU unit has two display interfaces.
Required properties for IPU:
- bypass_reset :Bypass reset to avoid display channel being.
stopped by probe since it may start to work in bootloader: 0 or 1.
- compatible : should be "fsl,imx6q-ipu".
- reg : the register address range.
- interrupts : the error and sync interrupts request.
- clocks : the clock sources that it depends on.
- clock-names: the related clock names.
- resets : IPU reset specifier. See reset.txt and fsl,imx-src.txt in
Documentation/devicetree/bindings/reset/ for details.
Required properties for fb:
- compatible : should be "fsl,mxc_sdc_fb".
- disp_dev : display device: "ldb", "lcd", "hdmi", "mipi_dsi".
- mode_str : "CLAA-WVGA" for lcd, "TRULY-WVGA" for TRULY mipi_dsi lcd panel,
"1920x1080M@60" for hdmi.
- default_bpp : default bits per pixel: 8/16/24/32
- int_clk : use internal clock as pixel clock: 0 or 1
- late_init : to avoid display channel being re-initialized
as we've probably setup the channel in bootloader: 0 or 1
- interface_pix_fmt : display interface pixel format as below:
RGB666 IPU_PIX_FMT_RGB666
RGB565 IPU_PIX_FMT_RGB565
RGB24 IPU_PIX_FMT_RGB24
BGR24 IPU_PIX_FMT_BGR24
GBR24 IPU_PIX_FMT_GBR24
YUV444 IPU_PIX_FMT_YUV444
YUYV IPU_PIX_FMT_YUYV
UYVY IPU_PIX_FMT_UYVY
YVYV IPU_PIX_FMT_YVYU
VYUY IPU_PIX_FMT_VYUY
Required properties for display:
- compatible : should be "fsl,lcd" for lcd panel
- reg : the register address range if necessary to have.
- interrupts : the error and sync interrupts if necessary to have.
- clocks : the clock sources that it depends on if necessary to have.
- clock-names: the related clock names if necessary to have.
- ipu_id : ipu id for the first display device: 0 or 1
- disp_id : display interface id for the first display interface: 0 or 1
- default_ifmt : save as above display interface pixel format for lcd
- pinctrl-names : should be "default"
- pinctrl-0 : should be pinctrl_ipu1_1 or pinctrl_ipu2_1, which depends on the
IPU connected.
- gpr : the mux controller for the display engine's display interfaces and the display encoder
(only valid for mipi dsi now).
- disp-power-on-supply : the regulator to control display panel's power.
(only valid for mipi dsi now).
- resets : the gpio pin to reset the display device(only valid for mipi display panel now).
- lcd_panel : the video mode name for the display device(only valid for mipi display panel now).
- dev_id : the display engine's identity within the system, which intends to replace ipu_id
(only valid for mipi dsi now).
Example for IPU:
ipu1: ipu@02400000 {
compatible = "fsl,imx6q-ipu";
reg = <0x02400000 0x400000>;
interrupts = <0 6 0x4 0 5 0x4>;
clocks = <&clks 130>, <&clks 131>, <&clks 132>,
<&clks 39>, <&clks 40>,
<&clks 135>, <&clks 136>;
clock-names = "bus", "di0", "di1",
"di0_sel", "di1_sel",
"ldb_di0", "ldb_di1";
resets = <&src 2>;
bypass_reset = <0>;
};
Example for fb:
fb0 {
compatible = "fsl,mxc_sdc_fb";
disp_dev = "ldb";
interface_pix_fmt = "RGB666";
mode_str ="LDB-XGA";
default_bpp = <16>;
int_clk = <0>;
late_init = <0>;
status = "okay";
};
Example for mipi dsi display:
mipi_dsi: mipi@021e0000 {
compatible = "fsl,imx6q-mipi-dsi";
reg = <0x021e0000 0x4000>;
interrupts = <0 102 0x04>;
gpr = <&gpr>;
clocks = <&clks 138>, <&clks 204>;
clock-names = "mipi_pllref_clk", "mipi_cfg_clk";
dev_id = <0>;
disp_id = <0>;
lcd_panel = "TRULY-WVGA";
disp-power-on-supply = <&reg_mipi_dsi_pwr_on>
resets = <&mipi_dsi_reset>;
status = "okay";
};

View File

@ -394,6 +394,13 @@ config MFD_MX25_TSADC
i.MX25 processors. They consist of a conversion queue for general
purpose ADC and a queue for Touchscreens.
config MFD_MXC_HDMI
tristate "Freescale HDMI Core"
select MFD_CORE
help
This is the core driver for the Freescale i.MX6 on-chip HDMI.
This MFD driver connects with the video and audio drivers for HDMI.
config MFD_HI6421_PMIC
tristate "HiSilicon Hi6421 PMU/Codec IC"
depends on OF

View File

@ -214,6 +214,7 @@ obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o
obj-$(CONFIG_MFD_DLN2) += dln2.o
obj-$(CONFIG_MFD_RT5033) += rt5033.o
obj-$(CONFIG_MFD_SKY81452) += sky81452.o
obj-$(CONFIG_MFD_MXC_HDMI) += mxc-hdmi-core.o
intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o

View File

@ -0,0 +1,808 @@
/*
* Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/regulator/machine.h>
#include <asm/mach-types.h>
#include <video/mxc_hdmi.h>
#include <linux/ipu-v3.h>
#include <video/mxc_edid.h>
#include "../mxc/ipu3/ipu_prv.h"
#include <linux/mfd/mxc-hdmi-core.h>
#include <linux/of_device.h>
#include <linux/mod_devicetable.h>
#include <linux/mfd/mxc-hdmi-core.h>
struct mxc_hdmi_data {
struct platform_device *pdev;
unsigned long __iomem *reg_base;
unsigned long reg_phys_base;
struct device *dev;
};
static void __iomem *hdmi_base;
static struct clk *isfr_clk;
static struct clk *iahb_clk;
static struct clk *mipi_core_clk;
static spinlock_t irq_spinlock;
static spinlock_t edid_spinlock;
static unsigned int sample_rate;
static unsigned long pixel_clk_rate;
static struct clk *pixel_clk;
static int hdmi_ratio;
int mxc_hdmi_ipu_id;
int mxc_hdmi_disp_id;
static struct mxc_edid_cfg hdmi_core_edid_cfg;
static int hdmi_core_init;
static unsigned int hdmi_dma_running;
static struct snd_pcm_substream *hdmi_audio_stream_playback;
static unsigned int hdmi_cable_state;
static unsigned int hdmi_blank_state;
static unsigned int hdmi_abort_state;
static spinlock_t hdmi_audio_lock, hdmi_blank_state_lock, hdmi_cable_state_lock;
unsigned int hdmi_set_cable_state(unsigned int state)
{
unsigned long flags;
struct snd_pcm_substream *substream = hdmi_audio_stream_playback;
spin_lock_irqsave(&hdmi_cable_state_lock, flags);
hdmi_cable_state = state;
spin_unlock_irqrestore(&hdmi_cable_state_lock, flags);
#ifndef CONFIG_MFD_MXC_HDMI_ANDROID
if (check_hdmi_state() && substream && hdmi_abort_state) {
hdmi_abort_state = 0;
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
}
#endif
return 0;
}
EXPORT_SYMBOL(hdmi_set_cable_state);
unsigned int hdmi_set_blank_state(unsigned int state)
{
unsigned long flags;
struct snd_pcm_substream *substream = hdmi_audio_stream_playback;
spin_lock_irqsave(&hdmi_blank_state_lock, flags);
hdmi_blank_state = state;
spin_unlock_irqrestore(&hdmi_blank_state_lock, flags);
#ifndef CONFIG_MFD_MXC_HDMI_ANDROID
if (check_hdmi_state() && substream && hdmi_abort_state) {
hdmi_abort_state = 0;
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
}
#endif
return 0;
}
EXPORT_SYMBOL(hdmi_set_blank_state);
static void hdmi_audio_abort_stream(struct snd_pcm_substream *substream)
{
unsigned long flags;
snd_pcm_stream_lock_irqsave(substream, flags);
#ifndef CONFIG_MFD_MXC_HDMI_ANDROID
if (snd_pcm_running(substream)) {
hdmi_abort_state = 1;
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
}
#else
if (snd_pcm_running(substream))
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
#endif
snd_pcm_stream_unlock_irqrestore(substream, flags);
}
int mxc_hdmi_abort_stream(void)
{
unsigned long flags;
spin_lock_irqsave(&hdmi_audio_lock, flags);
if (hdmi_audio_stream_playback)
hdmi_audio_abort_stream(hdmi_audio_stream_playback);
spin_unlock_irqrestore(&hdmi_audio_lock, flags);
return 0;
}
EXPORT_SYMBOL(mxc_hdmi_abort_stream);
int check_hdmi_state(void)
{
unsigned long flags1, flags2;
unsigned int ret;
spin_lock_irqsave(&hdmi_cable_state_lock, flags1);
spin_lock_irqsave(&hdmi_blank_state_lock, flags2);
ret = hdmi_cable_state && hdmi_blank_state;
spin_unlock_irqrestore(&hdmi_blank_state_lock, flags2);
spin_unlock_irqrestore(&hdmi_cable_state_lock, flags1);
return ret;
}
EXPORT_SYMBOL(check_hdmi_state);
int mxc_hdmi_register_audio(struct snd_pcm_substream *substream)
{
unsigned long flags, flags1;
int ret = 0;
snd_pcm_stream_lock_irqsave(substream, flags);
if (substream && check_hdmi_state()) {
spin_lock_irqsave(&hdmi_audio_lock, flags1);
if (hdmi_audio_stream_playback) {
pr_err("%s unconsist hdmi auido stream!\n", __func__);
ret = -EINVAL;
}
hdmi_audio_stream_playback = substream;
hdmi_abort_state = 0;
spin_unlock_irqrestore(&hdmi_audio_lock, flags1);
} else
ret = -EINVAL;
snd_pcm_stream_unlock_irqrestore(substream, flags);
return ret;
}
EXPORT_SYMBOL(mxc_hdmi_register_audio);
void mxc_hdmi_unregister_audio(struct snd_pcm_substream *substream)
{
unsigned long flags;
spin_lock_irqsave(&hdmi_audio_lock, flags);
hdmi_audio_stream_playback = NULL;
hdmi_abort_state = 0;
spin_unlock_irqrestore(&hdmi_audio_lock, flags);
}
EXPORT_SYMBOL(mxc_hdmi_unregister_audio);
u8 hdmi_readb(unsigned int reg)
{
u8 value;
value = __raw_readb(hdmi_base + reg);
return value;
}
EXPORT_SYMBOL(hdmi_readb);
#ifdef DEBUG
static bool overflow_lo;
static bool overflow_hi;
bool hdmi_check_overflow(void)
{
u8 val, lo, hi;
val = hdmi_readb(HDMI_IH_FC_STAT2);
lo = (val & HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW) != 0;
hi = (val & HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW) != 0;
if ((lo != overflow_lo) || (hi != overflow_hi)) {
pr_debug("%s LowPriority=%d HighPriority=%d <=======================\n",
__func__, lo, hi);
overflow_lo = lo;
overflow_hi = hi;
return true;
}
return false;
}
#else
bool hdmi_check_overflow(void)
{
return false;
}
#endif
EXPORT_SYMBOL(hdmi_check_overflow);
void hdmi_writeb(u8 value, unsigned int reg)
{
hdmi_check_overflow();
__raw_writeb(value, hdmi_base + reg);
hdmi_check_overflow();
}
EXPORT_SYMBOL(hdmi_writeb);
void hdmi_mask_writeb(u8 data, unsigned int reg, u8 shift, u8 mask)
{
u8 value = hdmi_readb(reg) & ~mask;
value |= (data << shift) & mask;
hdmi_writeb(value, reg);
}
EXPORT_SYMBOL(hdmi_mask_writeb);
unsigned int hdmi_read4(unsigned int reg)
{
/* read a four byte address from registers */
return (hdmi_readb(reg + 3) << 24) |
(hdmi_readb(reg + 2) << 16) |
(hdmi_readb(reg + 1) << 8) |
hdmi_readb(reg);
}
EXPORT_SYMBOL(hdmi_read4);
void hdmi_write4(unsigned int value, unsigned int reg)
{
/* write a four byte address to hdmi regs */
hdmi_writeb(value & 0xff, reg);
hdmi_writeb((value >> 8) & 0xff, reg + 1);
hdmi_writeb((value >> 16) & 0xff, reg + 2);
hdmi_writeb((value >> 24) & 0xff, reg + 3);
}
EXPORT_SYMBOL(hdmi_write4);
static void initialize_hdmi_ih_mutes(void)
{
u8 ih_mute;
/*
* Boot up defaults are:
* HDMI_IH_MUTE = 0x03 (disabled)
* HDMI_IH_MUTE_* = 0x00 (enabled)
*/
/* Disable top level interrupt bits in HDMI block */
ih_mute = hdmi_readb(HDMI_IH_MUTE) |
HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
hdmi_writeb(ih_mute, HDMI_IH_MUTE);
/* by default mask all interrupts */
hdmi_writeb(0xff, HDMI_VP_MASK);
hdmi_writeb(0xff, HDMI_FC_MASK0);
hdmi_writeb(0xff, HDMI_FC_MASK1);
hdmi_writeb(0xff, HDMI_FC_MASK2);
hdmi_writeb(0xff, HDMI_PHY_MASK0);
hdmi_writeb(0xff, HDMI_PHY_I2CM_INT_ADDR);
hdmi_writeb(0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
hdmi_writeb(0xff, HDMI_AUD_INT);
hdmi_writeb(0xff, HDMI_AUD_SPDIFINT);
hdmi_writeb(0xff, HDMI_AUD_HBR_MASK);
hdmi_writeb(0xff, HDMI_GP_MASK);
hdmi_writeb(0xff, HDMI_A_APIINTMSK);
hdmi_writeb(0xff, HDMI_CEC_MASK);
hdmi_writeb(0xff, HDMI_I2CM_INT);
hdmi_writeb(0xff, HDMI_I2CM_CTLINT);
/* Disable interrupts in the IH_MUTE_* registers */
hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT0);
hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT1);
hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT2);
hdmi_writeb(0xff, HDMI_IH_MUTE_AS_STAT0);
hdmi_writeb(0xff, HDMI_IH_MUTE_PHY_STAT0);
hdmi_writeb(0xff, HDMI_IH_MUTE_I2CM_STAT0);
hdmi_writeb(0xff, HDMI_IH_MUTE_CEC_STAT0);
hdmi_writeb(0xff, HDMI_IH_MUTE_VP_STAT0);
hdmi_writeb(0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
hdmi_writeb(0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
/* Enable top level interrupt bits in HDMI block */
ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
hdmi_writeb(ih_mute, HDMI_IH_MUTE);
}
static void hdmi_set_clock_regenerator_n(unsigned int value)
{
u8 val;
if (!hdmi_dma_running) {
hdmi_writeb(value & 0xff, HDMI_AUD_N1);
hdmi_writeb(0, HDMI_AUD_N2);
hdmi_writeb(0, HDMI_AUD_N3);
}
hdmi_writeb(value & 0xff, HDMI_AUD_N1);
hdmi_writeb((value >> 8) & 0xff, HDMI_AUD_N2);
hdmi_writeb((value >> 16) & 0x0f, HDMI_AUD_N3);
/* nshift factor = 0 */
val = hdmi_readb(HDMI_AUD_CTS3);
val &= ~HDMI_AUD_CTS3_N_SHIFT_MASK;
hdmi_writeb(val, HDMI_AUD_CTS3);
}
static void hdmi_set_clock_regenerator_cts(unsigned int cts)
{
u8 val;
if (!hdmi_dma_running) {
hdmi_writeb(cts & 0xff, HDMI_AUD_CTS1);
hdmi_writeb(0, HDMI_AUD_CTS2);
hdmi_writeb(0, HDMI_AUD_CTS3);
}
/* Must be set/cleared first */
val = hdmi_readb(HDMI_AUD_CTS3);
val &= ~HDMI_AUD_CTS3_CTS_MANUAL;
hdmi_writeb(val, HDMI_AUD_CTS3);
hdmi_writeb(cts & 0xff, HDMI_AUD_CTS1);
hdmi_writeb((cts >> 8) & 0xff, HDMI_AUD_CTS2);
hdmi_writeb(((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
}
static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
unsigned int ratio)
{
unsigned int n = (128 * freq) / 1000;
switch (freq) {
case 32000:
if (pixel_clk == 25174000)
n = (ratio == 150) ? 9152 : 4576;
else if (pixel_clk == 27020000)
n = (ratio == 150) ? 8192 : 4096;
else if (pixel_clk == 74170000 || pixel_clk == 148350000)
n = 11648;
else if (pixel_clk == 297000000)
n = (ratio == 150) ? 6144 : 3072;
else
n = 4096;
break;
case 44100:
if (pixel_clk == 25174000)
n = 7007;
else if (pixel_clk == 74170000)
n = 17836;
else if (pixel_clk == 148350000)
n = (ratio == 150) ? 17836 : 8918;
else if (pixel_clk == 297000000)
n = (ratio == 150) ? 9408 : 4704;
else
n = 6272;
break;
case 48000:
if (pixel_clk == 25174000)
n = (ratio == 150) ? 9152 : 6864;
else if (pixel_clk == 27020000)
n = (ratio == 150) ? 8192 : 6144;
else if (pixel_clk == 74170000)
n = 11648;
else if (pixel_clk == 148350000)
n = (ratio == 150) ? 11648 : 5824;
else if (pixel_clk == 297000000)
n = (ratio == 150) ? 10240 : 5120;
else
n = 6144;
break;
case 88200:
n = hdmi_compute_n(44100, pixel_clk, ratio) * 2;
break;
case 96000:
n = hdmi_compute_n(48000, pixel_clk, ratio) * 2;
break;
case 176400:
n = hdmi_compute_n(44100, pixel_clk, ratio) * 4;
break;
case 192000:
n = hdmi_compute_n(48000, pixel_clk, ratio) * 4;
break;
default:
break;
}
return n;
}
static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
unsigned int ratio)
{
unsigned int cts = 0;
switch (freq) {
case 32000:
if (pixel_clk == 297000000) {
cts = 222750;
break;
} else if (pixel_clk == 25174000) {
cts = 28125;
break;
}
case 48000:
case 96000:
case 192000:
switch (pixel_clk) {
case 25200000:
case 27000000:
case 54000000:
case 74250000:
case 148500000:
cts = pixel_clk / 1000;
break;
case 297000000:
cts = 247500;
break;
case 25174000:
cts = 28125l;
break;
/*
* All other TMDS clocks are not supported by
* DWC_hdmi_tx. The TMDS clocks divided or
* multiplied by 1,001 coefficients are not
* supported.
*/
default:
break;
}
break;
case 44100:
case 88200:
case 176400:
switch (pixel_clk) {
case 25200000:
cts = 28000;
break;
case 25174000:
cts = 31250;
break;
case 27000000:
cts = 30000;
break;
case 54000000:
cts = 60000;
break;
case 74250000:
cts = 82500;
break;
case 148500000:
cts = 165000;
break;
case 297000000:
cts = 247500;
break;
default:
break;
}
break;
default:
break;
}
if (ratio == 100)
return cts;
else
return (cts * ratio) / 100;
}
static void hdmi_set_clk_regenerator(void)
{
unsigned int clk_n, clk_cts;
clk_n = hdmi_compute_n(sample_rate, pixel_clk_rate, hdmi_ratio);
clk_cts = hdmi_compute_cts(sample_rate, pixel_clk_rate, hdmi_ratio);
if (clk_cts == 0) {
pr_debug("%s: pixel clock not supported: %d\n",
__func__, (int)pixel_clk_rate);
return;
}
pr_debug("%s: samplerate=%d ratio=%d pixelclk=%d N=%d cts=%d\n",
__func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate,
clk_n, clk_cts);
hdmi_set_clock_regenerator_cts(clk_cts);
hdmi_set_clock_regenerator_n(clk_n);
}
static int hdmi_core_get_of_property(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int err;
int ipu_id, disp_id;
err = of_property_read_u32(np, "ipu_id", &ipu_id);
if (err) {
dev_dbg(&pdev->dev, "get of property ipu_id fail\n");
return err;
}
err = of_property_read_u32(np, "disp_id", &disp_id);
if (err) {
dev_dbg(&pdev->dev, "get of property disp_id fail\n");
return err;
}
mxc_hdmi_ipu_id = ipu_id;
mxc_hdmi_disp_id = disp_id;
return err;
}
/* Need to run this before phy is enabled the first time to prevent
* overflow condition in HDMI_IH_FC_STAT2 */
void hdmi_init_clk_regenerator(void)
{
if (pixel_clk_rate == 0) {
pixel_clk_rate = 74250000;
hdmi_set_clk_regenerator();
}
}
EXPORT_SYMBOL(hdmi_init_clk_regenerator);
void hdmi_clk_regenerator_update_pixel_clock(u32 pixclock)
{
if (!pixclock)
return;
/* Translate pixel clock in ps (pico seconds) to Hz */
pixel_clk_rate = PICOS2KHZ(pixclock) * 1000UL;
hdmi_set_clk_regenerator();
}
EXPORT_SYMBOL(hdmi_clk_regenerator_update_pixel_clock);
void hdmi_set_dma_mode(unsigned int dma_running)
{
hdmi_dma_running = dma_running;
hdmi_set_clk_regenerator();
}
EXPORT_SYMBOL(hdmi_set_dma_mode);
void hdmi_set_sample_rate(unsigned int rate)
{
sample_rate = rate;
}
EXPORT_SYMBOL(hdmi_set_sample_rate);
void hdmi_set_edid_cfg(struct mxc_edid_cfg *cfg)
{
unsigned long flags;
spin_lock_irqsave(&edid_spinlock, flags);
memcpy(&hdmi_core_edid_cfg, cfg, sizeof(struct mxc_edid_cfg));
spin_unlock_irqrestore(&edid_spinlock, flags);
}
EXPORT_SYMBOL(hdmi_set_edid_cfg);
void hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg)
{
unsigned long flags;
spin_lock_irqsave(&edid_spinlock, flags);
memcpy(cfg, &hdmi_core_edid_cfg, sizeof(struct mxc_edid_cfg));
spin_unlock_irqrestore(&edid_spinlock, flags);
}
EXPORT_SYMBOL(hdmi_get_edid_cfg);
void hdmi_set_registered(int registered)
{
hdmi_core_init = registered;
}
EXPORT_SYMBOL(hdmi_set_registered);
int hdmi_get_registered(void)
{
return hdmi_core_init;
}
EXPORT_SYMBOL(hdmi_get_registered);
static int mxc_hdmi_core_probe(struct platform_device *pdev)
{
struct mxc_hdmi_data *hdmi_data;
struct resource *res;
unsigned long flags;
int ret = 0;
#ifdef DEBUG
overflow_lo = false;
overflow_hi = false;
#endif
hdmi_core_init = 0;
hdmi_dma_running = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOENT;
ret = hdmi_core_get_of_property(pdev);
if (ret < 0) {
dev_err(&pdev->dev, "get hdmi of property fail\n");
return -ENOENT;
}
hdmi_data = devm_kzalloc(&pdev->dev, sizeof(struct mxc_hdmi_data), GFP_KERNEL);
if (!hdmi_data) {
dev_err(&pdev->dev, "Couldn't allocate mxc hdmi mfd device\n");
return -ENOMEM;
}
hdmi_data->pdev = pdev;
pixel_clk = NULL;
sample_rate = 48000;
pixel_clk_rate = 0;
hdmi_ratio = 100;
spin_lock_init(&irq_spinlock);
spin_lock_init(&edid_spinlock);
spin_lock_init(&hdmi_cable_state_lock);
spin_lock_init(&hdmi_blank_state_lock);
spin_lock_init(&hdmi_audio_lock);
spin_lock_irqsave(&hdmi_cable_state_lock, flags);
hdmi_cable_state = 0;
spin_unlock_irqrestore(&hdmi_cable_state_lock, flags);
spin_lock_irqsave(&hdmi_blank_state_lock, flags);
hdmi_blank_state = 0;
spin_unlock_irqrestore(&hdmi_blank_state_lock, flags);
spin_lock_irqsave(&hdmi_audio_lock, flags);
hdmi_audio_stream_playback = NULL;
hdmi_abort_state = 0;
spin_unlock_irqrestore(&hdmi_audio_lock, flags);
mipi_core_clk = clk_get(&hdmi_data->pdev->dev, "mipi_core");
if (IS_ERR(mipi_core_clk)) {
ret = PTR_ERR(mipi_core_clk);
dev_err(&hdmi_data->pdev->dev,
"Unable to get mipi core clk: %d\n", ret);
goto eclkg;
}
ret = clk_prepare_enable(mipi_core_clk);
if (ret < 0) {
dev_err(&pdev->dev, "Cannot enable mipi core clock: %d\n", ret);
goto eclke;
}
isfr_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_isfr");
if (IS_ERR(isfr_clk)) {
ret = PTR_ERR(isfr_clk);
dev_err(&hdmi_data->pdev->dev,
"Unable to get HDMI isfr clk: %d\n", ret);
goto eclkg1;
}
ret = clk_prepare_enable(isfr_clk);
if (ret < 0) {
dev_err(&pdev->dev, "Cannot enable HDMI clock: %d\n", ret);
goto eclke1;
}
pr_debug("%s isfr_clk:%d\n", __func__,
(int)clk_get_rate(isfr_clk));
iahb_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_iahb");
if (IS_ERR(iahb_clk)) {
ret = PTR_ERR(iahb_clk);
dev_err(&hdmi_data->pdev->dev,
"Unable to get HDMI iahb clk: %d\n", ret);
goto eclkg2;
}
ret = clk_prepare_enable(iahb_clk);
if (ret < 0) {
dev_err(&pdev->dev, "Cannot enable HDMI clock: %d\n", ret);
goto eclke2;
}
hdmi_data->reg_phys_base = res->start;
if (!request_mem_region(res->start, resource_size(res),
dev_name(&pdev->dev))) {
dev_err(&pdev->dev, "request_mem_region failed\n");
ret = -EBUSY;
goto emem;
}
hdmi_data->reg_base = ioremap(res->start, resource_size(res));
if (!hdmi_data->reg_base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto eirq;
}
hdmi_base = hdmi_data->reg_base;
pr_debug("\n%s hdmi hw base = 0x%08x\n\n", __func__, (int)res->start);
initialize_hdmi_ih_mutes();
/* Disable HDMI clocks until video/audio sub-drivers are initialized */
clk_disable_unprepare(isfr_clk);
clk_disable_unprepare(iahb_clk);
clk_disable_unprepare(mipi_core_clk);
/* Replace platform data coming in with a local struct */
platform_set_drvdata(pdev, hdmi_data);
return ret;
eirq:
release_mem_region(res->start, resource_size(res));
emem:
clk_disable_unprepare(iahb_clk);
eclke2:
clk_put(iahb_clk);
eclkg2:
clk_disable_unprepare(isfr_clk);
eclke1:
clk_put(isfr_clk);
eclkg1:
clk_disable_unprepare(mipi_core_clk);
eclke:
clk_put(mipi_core_clk);
eclkg:
return ret;
}
static int __exit mxc_hdmi_core_remove(struct platform_device *pdev)
{
struct mxc_hdmi_data *hdmi_data = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(hdmi_data->reg_base);
release_mem_region(res->start, resource_size(res));
return 0;
}
static const struct of_device_id imx_hdmi_dt_ids[] = {
{ .compatible = "fsl,imx6q-hdmi-core", },
{ .compatible = "fsl,imx6dl-hdmi-core", },
{ /* sentinel */ }
};
static struct platform_driver mxc_hdmi_core_driver = {
.driver = {
.name = "mxc_hdmi_core",
.of_match_table = imx_hdmi_dt_ids,
.owner = THIS_MODULE,
},
.remove = __exit_p(mxc_hdmi_core_remove),
};
static int __init mxc_hdmi_core_init(void)
{
return platform_driver_probe(&mxc_hdmi_core_driver,
mxc_hdmi_core_probe);
}
static void __exit mxc_hdmi_core_exit(void)
{
platform_driver_unregister(&mxc_hdmi_core_driver);
}
subsys_initcall(mxc_hdmi_core_init);
module_exit(mxc_hdmi_core_exit);
MODULE_DESCRIPTION("Core driver for Freescale i.Mx on-chip HDMI");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_LICENSE("GPL");

View File

@ -4,9 +4,18 @@ if ARCH_MXC
menu "MXC support drivers"
config MXC_IPU
bool "Image Processing Unit Driver"
select MXC_IPU_V3
help
If you plan to use the Image Processing unit, say
Y here. IPU is needed by Framebuffer and V4L2 drivers.
source "drivers/mxc/mlb/Kconfig"
source "drivers/mxc/ipu3/Kconfig"
source "drivers/mxc/sim/Kconfig"
source "drivers/mxc/vpu/Kconfig"
source "drivers/mxc/hdmi-cec/Kconfig"
endmenu

View File

@ -1,3 +1,5 @@
obj-$(CONFIG_MXC_MLB) += mlb/
obj-$(CONFIG_MXC_SIM) += sim/
obj-$(CONFIG_MXC_VPU) += vpu/
obj-$(CONFIG_MXC_IPU_V3) += ipu3/
obj-$(CONFIG_MXC_HDMI_CEC) += hdmi-cec/

View File

@ -0,0 +1,11 @@
menu "MXC HDMI CEC (Consumer Electronics Control) support"
config MXC_HDMI_CEC
tristate "Support for MXC HDMI CEC (Consumer Electronics Control)"
depends on MFD_MXC_HDMI
depends on FB_MXC_HDMI
help
The HDMI CEC device implement low level protocol on i.MX6x platforms.
endmenu

View File

@ -0,0 +1 @@
obj-$(CONFIG_MXC_HDMI_CEC) += mxc_hdmi-cec.o

View File

@ -0,0 +1,664 @@
/*
* Copyright (C) 2012-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file mxc_hdmi-cec.c
*
* @brief HDMI CEC system initialization and file operation implementation
*
* @ingroup HDMI
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/fsl_devices.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <linux/sizes.h>
#include <linux/console.h>
#include <linux/types.h>
#include <linux/mfd/mxc-hdmi-core.h>
#include <linux/pinctrl/consumer.h>
#include <video/mxc_hdmi.h>
#include "mxc_hdmi-cec.h"
#define MAX_MESSAGE_LEN 17
#define MESSAGE_TYPE_RECEIVE_SUCCESS 1
#define MESSAGE_TYPE_NOACK 2
#define MESSAGE_TYPE_DISCONNECTED 3
#define MESSAGE_TYPE_CONNECTED 4
#define MESSAGE_TYPE_SEND_SUCCESS 5
struct hdmi_cec_priv {
int receive_error;
int send_error;
u8 Logical_address;
bool cec_state;
u8 last_msg[MAX_MESSAGE_LEN];
u8 msg_len;
u8 latest_cec_stat;
spinlock_t irq_lock;
struct delayed_work hdmi_cec_work;
struct mutex lock;
};
struct hdmi_cec_event {
int event_type;
int msg_len;
u8 msg[MAX_MESSAGE_LEN];
struct list_head list;
};
static LIST_HEAD(head);
static int hdmi_cec_major;
static struct class *hdmi_cec_class;
static struct hdmi_cec_priv hdmi_cec_data;
static u8 open_count;
static wait_queue_head_t hdmi_cec_queue;
static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data)
{
struct hdmi_cec_priv *hdmi_cec = data;
u8 cec_stat = 0;
unsigned long flags;
spin_lock_irqsave(&hdmi_cec->irq_lock, flags);
hdmi_writeb(0x7f, HDMI_IH_MUTE_CEC_STAT0);
cec_stat = hdmi_readb(HDMI_IH_CEC_STAT0);
hdmi_writeb(cec_stat, HDMI_IH_CEC_STAT0);
if ((cec_stat & (HDMI_IH_CEC_STAT0_ERROR_INIT | \
HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | \
HDMI_IH_CEC_STAT0_DONE)) == 0) {
spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags);
return IRQ_HANDLED;
}
pr_debug("HDMI CEC interrupt received\n");
hdmi_cec->latest_cec_stat = cec_stat;
schedule_delayed_work(&(hdmi_cec->hdmi_cec_work), msecs_to_jiffies(20));
spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags);
return IRQ_HANDLED;
}
void mxc_hdmi_cec_handle(u16 cec_stat)
{
u8 val = 0, i = 0;
struct hdmi_cec_event *event = NULL;
/* The current transmission is successful (for initiator only). */
if (!open_count)
return;
if (cec_stat & HDMI_IH_CEC_STAT0_DONE) {
event = vmalloc(sizeof(struct hdmi_cec_event));
if (NULL == event) {
pr_err("%s: Not enough memory!\n", __func__);
return;
}
memset(event, 0, sizeof(struct hdmi_cec_event));
event->event_type = MESSAGE_TYPE_SEND_SUCCESS;
mutex_lock(&hdmi_cec_data.lock);
list_add_tail(&event->list, &head);
mutex_unlock(&hdmi_cec_data.lock);
wake_up(&hdmi_cec_queue);
}
/* EOM is detected so that the received data is ready
* in the receiver data buffer
*/
if (cec_stat & HDMI_IH_CEC_STAT0_EOM) {
hdmi_writeb(0x02, HDMI_IH_CEC_STAT0);
event = vmalloc(sizeof(struct hdmi_cec_event));
if (NULL == event) {
pr_err("%s: Not enough memory!\n", __func__);
return;
}
memset(event, 0, sizeof(struct hdmi_cec_event));
event->msg_len = hdmi_readb(HDMI_CEC_RX_CNT);
if (!event->msg_len) {
pr_err("%s: Invalid CEC message length!\n", __func__);
return;
}
event->event_type = MESSAGE_TYPE_RECEIVE_SUCCESS;
for (i = 0; i < event->msg_len; i++)
event->msg[i] = hdmi_readb(HDMI_CEC_RX_DATA0+i);
hdmi_writeb(0x0, HDMI_CEC_LOCK);
mutex_lock(&hdmi_cec_data.lock);
list_add_tail(&event->list, &head);
mutex_unlock(&hdmi_cec_data.lock);
wake_up(&hdmi_cec_queue);
}
/* An error is detected on cec line (for initiator only). */
if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_INIT) {
mutex_lock(&hdmi_cec_data.lock);
hdmi_cec_data.send_error++;
if (hdmi_cec_data.send_error > 5) {
pr_err("%s:Re-transmission is attempted more than 5 times!\n",
__func__);
hdmi_cec_data.send_error = 0;
mutex_unlock(&hdmi_cec_data.lock);
return;
}
for (i = 0; i < hdmi_cec_data.msg_len; i++) {
hdmi_writeb(hdmi_cec_data.last_msg[i],
HDMI_CEC_TX_DATA0 + i);
}
hdmi_writeb(hdmi_cec_data.msg_len, HDMI_CEC_TX_CNT);
val = hdmi_readb(HDMI_CEC_CTRL);
val |= 0x01;
hdmi_writeb(val, HDMI_CEC_CTRL);
mutex_unlock(&hdmi_cec_data.lock);
}
/* A frame is not acknowledged in a directly addressed message.
* Or a frame is negatively acknowledged in
* a broadcast message (for initiator only).
*/
if (cec_stat & HDMI_IH_CEC_STAT0_NACK) {
event = vmalloc(sizeof(struct hdmi_cec_event));
if (NULL == event) {
pr_err("%s: Not enough memory\n", __func__);
return;
}
memset(event, 0, sizeof(struct hdmi_cec_event));
event->event_type = MESSAGE_TYPE_NOACK;
mutex_lock(&hdmi_cec_data.lock);
list_add_tail(&event->list, &head);
mutex_unlock(&hdmi_cec_data.lock);
wake_up(&hdmi_cec_queue);
}
/* An error is notified by a follower.
* Abnormal logic data bit error (for follower).
*/
if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_FOLL) {
hdmi_cec_data.receive_error++;
}
/* HDMI cable connected */
if (cec_stat & 0x80) {
event = vmalloc(sizeof(struct hdmi_cec_event));
if (NULL == event) {
pr_err("%s: Not enough memory\n", __func__);
return;
}
memset(event, 0, sizeof(struct hdmi_cec_event));
event->event_type = MESSAGE_TYPE_CONNECTED;
mutex_lock(&hdmi_cec_data.lock);
list_add_tail(&event->list, &head);
mutex_unlock(&hdmi_cec_data.lock);
wake_up(&hdmi_cec_queue);
}
/* HDMI cable disconnected */
if (cec_stat & 0x100) {
event = vmalloc(sizeof(struct hdmi_cec_event));
if (NULL == event) {
pr_err("%s: Not enough memory!\n", __func__);
return;
}
memset(event, 0, sizeof(struct hdmi_cec_event));
event->event_type = MESSAGE_TYPE_DISCONNECTED;
mutex_lock(&hdmi_cec_data.lock);
list_add_tail(&event->list, &head);
mutex_unlock(&hdmi_cec_data.lock);
wake_up(&hdmi_cec_queue);
}
return;
}
EXPORT_SYMBOL(mxc_hdmi_cec_handle);
static void mxc_hdmi_cec_worker(struct work_struct *work)
{
u8 val;
mxc_hdmi_cec_handle(hdmi_cec_data.latest_cec_stat);
val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL |
HDMI_IH_CEC_STAT0_ARB_LOST;
hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0);
}
/*!
* @brief open function for vpu file operation
*
* @return 0 on success or negative error code on error
*/
static int hdmi_cec_open(struct inode *inode, struct file *filp)
{
mutex_lock(&hdmi_cec_data.lock);
if (open_count) {
mutex_unlock(&hdmi_cec_data.lock);
return -EBUSY;
}
open_count = 1;
filp->private_data = (void *)(&hdmi_cec_data);
hdmi_cec_data.Logical_address = 15;
hdmi_cec_data.cec_state = false;
mutex_unlock(&hdmi_cec_data.lock);
return 0;
}
static ssize_t hdmi_cec_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
struct hdmi_cec_event *event = NULL;
pr_debug("function : %s\n", __func__);
if (!open_count)
return -ENODEV;
mutex_lock(&hdmi_cec_data.lock);
if (false == hdmi_cec_data.cec_state) {
mutex_unlock(&hdmi_cec_data.lock);
return -EACCES;
}
mutex_unlock(&hdmi_cec_data.lock);
/* delete from list */
mutex_lock(&hdmi_cec_data.lock);
if (list_empty(&head)) {
mutex_unlock(&hdmi_cec_data.lock);
return -EACCES;
}
event = list_first_entry(&head, struct hdmi_cec_event, list);
list_del(&event->list);
mutex_unlock(&hdmi_cec_data.lock);
if (copy_to_user(buf, event,
sizeof(struct hdmi_cec_event) - sizeof(struct list_head))) {
vfree(event);
return -EFAULT;
}
vfree(event);
return sizeof(struct hdmi_cec_event);
}
static ssize_t hdmi_cec_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int ret = 0 , i = 0;
u8 msg[MAX_MESSAGE_LEN];
u8 msg_len = 0, val = 0;
pr_debug("function : %s\n", __func__);
if (!open_count)
return -ENODEV;
mutex_lock(&hdmi_cec_data.lock);
if (false == hdmi_cec_data.cec_state) {
mutex_unlock(&hdmi_cec_data.lock);
return -EACCES;
}
mutex_unlock(&hdmi_cec_data.lock);
if (count > MAX_MESSAGE_LEN)
return -EINVAL;
mutex_lock(&hdmi_cec_data.lock);
hdmi_cec_data.send_error = 0;
memset(&msg, 0, MAX_MESSAGE_LEN);
ret = copy_from_user(&msg, buf, count);
if (ret) {
ret = -EACCES;
goto end;
}
msg_len = count;
hdmi_writeb(msg_len, HDMI_CEC_TX_CNT);
for (i = 0; i < msg_len; i++) {
hdmi_writeb(msg[i], HDMI_CEC_TX_DATA0+i);
}
val = hdmi_readb(HDMI_CEC_CTRL);
val |= 0x01;
hdmi_writeb(val, HDMI_CEC_CTRL);
memcpy(hdmi_cec_data.last_msg, msg, msg_len);
hdmi_cec_data.msg_len = msg_len;
i = 0;
val = hdmi_readb(HDMI_CEC_CTRL);
while ((val & 0x01) == 0x1) {
msleep(50);
i++;
if (i > 3) {
ret = -EIO;
goto end;
}
val = hdmi_readb(HDMI_CEC_CTRL);
}
end:
mutex_unlock(&hdmi_cec_data.lock);
return ret;
}
/*!
* @brief IO ctrl function for vpu file operation
* @param cmd IO ctrl command
* @return 0 on success or negative error code on error
*/
static long hdmi_cec_ioctl(struct file *filp, u_int cmd,
u_long arg)
{
int ret = 0, status = 0;
u8 val = 0, msg = 0;
struct mxc_edid_cfg hdmi_edid_cfg;
pr_debug("function : %s\n", __func__);
if (!open_count)
return -ENODEV;
switch (cmd) {
case HDMICEC_IOC_SETLOGICALADDRESS:
mutex_lock(&hdmi_cec_data.lock);
if (false == hdmi_cec_data.cec_state) {
mutex_unlock(&hdmi_cec_data.lock);
return -EACCES;
}
hdmi_cec_data.Logical_address = (u8)arg;
if (hdmi_cec_data.Logical_address <= 7) {
val = 1 << hdmi_cec_data.Logical_address;
hdmi_writeb(val, HDMI_CEC_ADDR_L);
hdmi_writeb(0, HDMI_CEC_ADDR_H);
} else if (hdmi_cec_data.Logical_address > 7 &&
hdmi_cec_data.Logical_address <= 15) {
val = 1 << (hdmi_cec_data.Logical_address - 8);
hdmi_writeb(val, HDMI_CEC_ADDR_H);
hdmi_writeb(0, HDMI_CEC_ADDR_L);
} else {
ret = -EINVAL;
}
/* Send Polling message with same source
* and destination address
*/
if (0 == ret && 15 != hdmi_cec_data.Logical_address) {
msg = (hdmi_cec_data.Logical_address << 4) |
hdmi_cec_data.Logical_address;
hdmi_writeb(1, HDMI_CEC_TX_CNT);
hdmi_writeb(msg, HDMI_CEC_TX_DATA0);
val = hdmi_readb(HDMI_CEC_CTRL);
val |= 0x01;
hdmi_writeb(val, HDMI_CEC_CTRL);
}
mutex_unlock(&hdmi_cec_data.lock);
break;
case HDMICEC_IOC_STARTDEVICE:
val = hdmi_readb(HDMI_MC_CLKDIS);
val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
hdmi_writeb(val, HDMI_MC_CLKDIS);
hdmi_writeb(0x02, HDMI_CEC_CTRL);
val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK |
HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE;
hdmi_writeb(val, HDMI_CEC_POLARITY);
val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL |
HDMI_IH_CEC_STAT0_ARB_LOST;
hdmi_writeb(val, HDMI_CEC_MASK);
hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0);
mutex_lock(&hdmi_cec_data.lock);
hdmi_cec_data.cec_state = true;
mutex_unlock(&hdmi_cec_data.lock);
break;
case HDMICEC_IOC_STOPDEVICE:
hdmi_writeb(0x10, HDMI_CEC_CTRL);
val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL |
HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST |
HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM |
HDMI_IH_CEC_STAT0_DONE;
hdmi_writeb(val, HDMI_CEC_MASK);
hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0);
hdmi_writeb(0x0, HDMI_CEC_POLARITY);
val = hdmi_readb(HDMI_MC_CLKDIS);
val |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
hdmi_writeb(val, HDMI_MC_CLKDIS);
mutex_lock(&hdmi_cec_data.lock);
hdmi_cec_data.cec_state = false;
mutex_unlock(&hdmi_cec_data.lock);
break;
case HDMICEC_IOC_GETPHYADDRESS:
hdmi_get_edid_cfg(&hdmi_edid_cfg);
status = copy_to_user((void __user *)arg,
&hdmi_edid_cfg.physical_address,
4*sizeof(u8));
if (status)
ret = -EFAULT;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/*!
* @brief Release function for vpu file operation
* @return 0 on success or negative error code on error
*/
static int hdmi_cec_release(struct inode *inode, struct file *filp)
{
mutex_lock(&hdmi_cec_data.lock);
if (open_count) {
open_count = 0;
hdmi_cec_data.cec_state = false;
hdmi_cec_data.Logical_address = 15;
}
mutex_unlock(&hdmi_cec_data.lock);
return 0;
}
static unsigned int hdmi_cec_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
pr_debug("function : %s\n", __func__);
if (!open_count)
return -ENODEV;
if (false == hdmi_cec_data.cec_state)
return -EACCES;
poll_wait(file, &hdmi_cec_queue, wait);
if (!list_empty(&head))
mask |= (POLLIN | POLLRDNORM);
return mask;
}
const struct file_operations hdmi_cec_fops = {
.owner = THIS_MODULE,
.read = hdmi_cec_read,
.write = hdmi_cec_write,
.open = hdmi_cec_open,
.unlocked_ioctl = hdmi_cec_ioctl,
.release = hdmi_cec_release,
.poll = hdmi_cec_poll,
};
static int hdmi_cec_dev_probe(struct platform_device *pdev)
{
int err = 0;
struct device *temp_class;
struct resource *res;
struct pinctrl *pinctrl;
int irq = platform_get_irq(pdev, 0);
hdmi_cec_major = register_chrdev(hdmi_cec_major,
"mxc_hdmi_cec", &hdmi_cec_fops);
if (hdmi_cec_major < 0) {
dev_err(&pdev->dev, "hdmi_cec: unable to get a major for HDMI CEC\n");
err = -EBUSY;
goto out;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (unlikely(res == NULL)) {
dev_err(&pdev->dev, "hdmi_cec:No HDMI irq line provided\n");
goto err_out_chrdev;
}
spin_lock_init(&hdmi_cec_data.irq_lock);
err = devm_request_irq(&pdev->dev, irq, mxc_hdmi_cec_isr, IRQF_SHARED,
dev_name(&pdev->dev), &hdmi_cec_data);
if (err < 0) {
dev_err(&pdev->dev, "hdmi_cec:Unable to request irq: %d\n", err);
goto err_out_chrdev;
}
hdmi_cec_class = class_create(THIS_MODULE, "mxc_hdmi_cec");
if (IS_ERR(hdmi_cec_class)) {
err = PTR_ERR(hdmi_cec_class);
goto err_out_chrdev;
}
temp_class = device_create(hdmi_cec_class, NULL,
MKDEV(hdmi_cec_major, 0), NULL, "mxc_hdmi_cec");
if (IS_ERR(temp_class)) {
err = PTR_ERR(temp_class);
goto err_out_class;
}
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
dev_err(&pdev->dev, "can't get/select CEC pinctrl\n");
goto err_out_class;
}
init_waitqueue_head(&hdmi_cec_queue);
INIT_LIST_HEAD(&head);
mutex_init(&hdmi_cec_data.lock);
hdmi_cec_data.Logical_address = 15;
platform_set_drvdata(pdev, &hdmi_cec_data);
INIT_DELAYED_WORK(&hdmi_cec_data.hdmi_cec_work, mxc_hdmi_cec_worker);
dev_info(&pdev->dev, "HDMI CEC initialized\n");
goto out;
err_out_class:
device_destroy(hdmi_cec_class, MKDEV(hdmi_cec_major, 0));
class_destroy(hdmi_cec_class);
err_out_chrdev:
unregister_chrdev(hdmi_cec_major, "mxc_hdmi_cec");
out:
return err;
}
static int hdmi_cec_dev_remove(struct platform_device *pdev)
{
if (hdmi_cec_major > 0) {
device_destroy(hdmi_cec_class, MKDEV(hdmi_cec_major, 0));
class_destroy(hdmi_cec_class);
unregister_chrdev(hdmi_cec_major, "mxc_hdmi_cec");
hdmi_cec_major = 0;
}
return 0;
}
static const struct of_device_id imx_hdmi_cec_match[] = {
{ .compatible = "fsl,imx6q-hdmi-cec", },
{ .compatible = "fsl,imx6dl-hdmi-cec", },
{ /* sentinel */ }
};
static struct platform_driver mxc_hdmi_cec_driver = {
.probe = hdmi_cec_dev_probe,
.remove = hdmi_cec_dev_remove,
.driver = {
.name = "mxc_hdmi_cec",
.of_match_table = imx_hdmi_cec_match,
},
};
module_platform_driver(mxc_hdmi_cec_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("Linux HDMI CEC driver for Freescale i.MX/MXC");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:mxc_hdmi_cec");

View File

@ -0,0 +1,38 @@
/*
* Copyright 2005-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef _HDMICEC_H_
#define _HDMICEC_H_
#include <linux/ioctl.h>
/*
* Ioctl definitions
*/
/* Use 'k' as magic number */
#define HDMICEC_IOC_MAGIC 'H'
/*
* S means "Set" through a ptr,
* T means "Tell" directly with the argument value
* G means "Get": reply by setting through a pointer
* Q means "Query": response is on the return value
* X means "eXchange": G and S atomically
* H means "sHift": T and Q atomically
*/
#define HDMICEC_IOC_SETLOGICALADDRESS \
_IOW(HDMICEC_IOC_MAGIC, 1, unsigned char)
#define HDMICEC_IOC_STARTDEVICE _IO(HDMICEC_IOC_MAGIC, 2)
#define HDMICEC_IOC_STOPDEVICE _IO(HDMICEC_IOC_MAGIC, 3)
#define HDMICEC_IOC_GETPHYADDRESS \
_IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4])
#endif /* !_HDMICEC_H_ */

View File

@ -0,0 +1,21 @@
config MXC_IPU_V3
bool
config MXC_IPU_V3_PRG
tristate "i.MX IPUv3 prefetch gasket engine"
depends on MXC_IPU_V3 && MXC_IPU_V3_PRE
help
This enables support for the IPUv3 prefetch gasket engine to
support double buffer handshake control bewteen IPUv3 and
prefetch engine(PRE), snoop the AXI interface for display
refresh requests to memory and modify the request address to
fetch the double buffered row of blocks in OCRAM.
config MXC_IPU_V3_PRE
tristate "i.MX IPUv3 prefetch engine"
depends on MXC_IPU_V3
select MXC_IPU_V3_PRG
help
This enables support for the IPUv3 prefetch engine to improve
the system memory performance. The engine has the capability
to resolve framebuffers in tile pixel format to linear.

View File

@ -0,0 +1,7 @@
obj-$(CONFIG_MXC_IPU_V3) = mxc_ipu.o
obj-$(CONFIG_MXC_IPU_V3_PRG) += prg.o
obj-$(CONFIG_MXC_IPU_V3_PRE) += pre.o
mxc_ipu-objs := ipu_common.o ipu_ic.o ipu_disp.o ipu_capture.o ipu_device.o \
ipu_calc_stripes_sizes.o vdoa.o ipu_pixel_clk.o

View File

@ -0,0 +1,495 @@
/*
* Copyright 2009-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*
* @file ipu_calc_stripes_sizes.c
*
* @brief IPU IC functions
*
* @ingroup IPU
*/
#include <linux/ipu-v3.h>
#include <linux/module.h>
#include <linux/math64.h>
#define BPP_32 0
#define BPP_16 3
#define BPP_8 5
#define BPP_24 1
#define BPP_12 4
#define BPP_18 2
static u32 truncate(u32 up, /* 0: down; else: up */
u64 a, /* must be non-negative */
u32 b)
{
u32 d;
u64 div;
div = div_u64(a, b);
d = b * (div >> 32);
if (up && (a > (((u64)d) << 32)))
return d+b;
else
return d;
}
static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write)
{/* return input_f */
unsigned int f_calculated = 0;
switch (pfs) {
case IPU_PIX_FMT_YVU422P:
case IPU_PIX_FMT_YUV422P:
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
case IPU_PIX_FMT_YVU420P:
case IPU_PIX_FMT_YUV444P:
f_calculated = 16;
break;
case IPU_PIX_FMT_RGB565:
case IPU_PIX_FMT_YUYV:
case IPU_PIX_FMT_UYVY:
f_calculated = 8;
break;
case IPU_PIX_FMT_NV12:
f_calculated = 8;
break;
default:
f_calculated = 0;
break;
}
if (!f_calculated) {
switch (bpp) {
case BPP_32:
f_calculated = 2;
break;
case BPP_16:
f_calculated = 4;
break;
case BPP_8:
case BPP_24:
f_calculated = 8;
break;
case BPP_12:
f_calculated = 16;
break;
case BPP_18:
f_calculated = 32;
break;
default:
f_calculated = 0;
break;
}
}
return f_calculated;
}
static unsigned int m_calc(unsigned int pfs)
{
unsigned int m_calculated = 0;
switch (pfs) {
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
case IPU_PIX_FMT_YVU422P:
case IPU_PIX_FMT_YUV422P:
case IPU_PIX_FMT_YVU420P:
case IPU_PIX_FMT_YUV444P:
m_calculated = 16;
break;
case IPU_PIX_FMT_NV12:
case IPU_PIX_FMT_YUYV:
case IPU_PIX_FMT_UYVY:
m_calculated = 8;
break;
default:
m_calculated = 8;
break;
}
return m_calculated;
}
static int calc_split_resize_coeffs(unsigned int inSize, unsigned int outSize,
unsigned int *resizeCoeff,
unsigned int *downsizeCoeff)
{
uint32_t tempSize;
uint32_t tempDownsize;
if (inSize > 4096) {
pr_debug("IC input size(%d) cannot exceed 4096\n",
inSize);
return -EINVAL;
}
if (outSize > 1024) {
pr_debug("IC output size(%d) cannot exceed 1024\n",
outSize);
return -EINVAL;
}
if ((outSize << 3) < inSize) {
pr_debug("IC cannot downsize more than 8:1\n");
return -EINVAL;
}
/* Compute downsizing coefficient */
/* Output of downsizing unit cannot be more than 1024 */
tempDownsize = 0;
tempSize = inSize;
while (((tempSize > 1024) || (tempSize >= outSize * 2)) &&
(tempDownsize < 2)) {
tempSize >>= 1;
tempDownsize++;
}
*downsizeCoeff = tempDownsize;
/* compute resizing coefficient using the following equation:
resizeCoeff = M*(SI -1)/(SO - 1)
where M = 2^13, SI - input size, SO - output size */
*resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
if (*resizeCoeff >= 16384L) {
pr_debug("Overflow on IC resize coefficient.\n");
return -EINVAL;
}
pr_debug("resizing from %u -> %u pixels, "
"downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
*downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);
return 0;
}
/* Stripe parameters calculator */
/**************************************************************************
Notes:
MSW = the maximal width allowed for a stripe
i.MX31: 720, i.MX35: 800, i.MX37/51/53: 1024
cirr = the maximal inverse resizing ratio for which overlap in the input
is requested; typically cirr~2
flags
bit 0 - equal_stripes
0 each stripe is allowed to have independent parameters
for maximal image quality
1 the stripes are requested to have identical parameters
(except the base address), for maximal performance
bit 1 - vertical/horizontal
0 horizontal
1 vertical
If performance is the top priority (above image quality)
Avoid overlap, by setting CIRR = 0
This will also force effectively identical_stripes = 1
Choose IF & OF that corresponds to the same IOX/SX for both stripes
Choose IFW & OFW such that
IFW/IM, IFW/IF, OFW/OM, OFW/OF are even integers
The function returns an error status:
0: no error
1: invalid input parameters -> aborted without result
Valid parameters should satisfy the following conditions
IFW <= OFW, otherwise downsizing is required
- which is not supported yet
4 <= IFW,OFW, so some interpolation may be needed even without overlap
IM, OM, IF, OF should not vanish
2*IF <= IFW
so the frame can be split to two equal stripes, even without overlap
2*(OF+IF/irr_opt) <= OFW
so a valid positive INW exists even for equal stripes
OF <= MSW, otherwise, the left stripe cannot be sufficiently large
MSW < OFW, so splitting to stripes is required
OFW <= 2*MSW, so two stripes are sufficient
(this also implies that 2<=MSW)
2: OF is not a multiple of OM - not fully-supported yet
Output is produced but OW is not guaranited to be a multiple of OM
4: OFW reduced to be a multiple of OM
8: CIRR > 1: truncated to 1
Overlap is not supported (and not needed) y for upsizing)
**************************************************************************/
int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
/* input frame width;>1 */
unsigned int output_frame_width, /* output frame width; >1 */
const unsigned int maximal_stripe_width,
/* the maximal width allowed for a stripe */
const unsigned long long cirr, /* see above */
const unsigned int flags, /* see above */
u32 input_pixelformat,/* pixel format after of read channel*/
u32 output_pixelformat,/* pixel format after of write channel*/
struct stripe_param *left,
struct stripe_param *right)
{
const unsigned int irr_frac_bits = 13;
const unsigned long irr_steps = 1 << irr_frac_bits;
const u64 dirr = ((u64)1) << (32 - 2);
/* The maximum relative difference allowed between the irrs */
const u64 cr = ((u64)4) << 32;
/* The importance ratio between the two terms in the cost function below */
unsigned int status;
unsigned int temp;
unsigned int onw_min;
unsigned int inw = 0, onw = 0, inw_best = 0;
/* number of pixels in the left stripe NOT hidden by the right stripe */
u64 irr_opt; /* the optimal inverse resizing ratio */
u64 rr_opt; /* the optimal resizing ratio = 1/irr_opt*/
u64 dinw; /* the misalignment between the stripes */
/* (measured in units of input columns) */
u64 difwl, difwr = 0;
/* The number of input columns not reflected in the output */
/* the resizing ratio used for the right stripe is */
/* left->irr and right->irr respectively */
u64 cost, cost_min;
u64 div; /* result of division */
bool equal_stripes = (flags & 0x1) != 0;
bool vertical = (flags & 0x2) != 0;
unsigned int input_m, input_f, output_m, output_f; /* parameters for upsizing by stripes */
unsigned int resize_coeff;
unsigned int downsize_coeff;
status = 0;
if (vertical) {
input_f = 2;
input_m = 8;
output_f = 8;
output_m = 2;
} else {
input_f = f_calc(input_pixelformat, 0, NULL);
input_m = m_calc(input_pixelformat);
output_f = input_m;
output_m = m_calc(output_pixelformat);
}
if ((input_frame_width < 4) || (output_frame_width < 4))
return 1;
irr_opt = div_u64((((u64)(input_frame_width - 1)) << 32),
(output_frame_width - 1));
rr_opt = div_u64((((u64)(output_frame_width - 1)) << 32),
(input_frame_width - 1));
if ((input_m == 0) || (output_m == 0) || (input_f == 0) || (output_f == 0)
|| (input_frame_width < (2 * input_f))
|| ((((u64)output_frame_width) << 32) <
(2 * ((((u64)output_f) << 32) + (input_f * rr_opt))))
|| (maximal_stripe_width < output_f)
|| ((output_frame_width <= maximal_stripe_width)
&& (equal_stripes == 0))
|| ((2 * maximal_stripe_width) < output_frame_width))
return 1;
if (output_f % output_m)
status += 2;
temp = truncate(0, (((u64)output_frame_width) << 32), output_m);
if (temp < output_frame_width) {
output_frame_width = temp;
status += 4;
}
pr_debug("---------------->\n"
"if = %d\n"
"im = %d\n"
"of = %d\n"
"om = %d\n"
"irr_opt = %llu\n"
"rr_opt = %llu\n"
"cirr = %llu\n"
"pixel in = %08x\n"
"pixel out = %08x\n"
"ifw = %d\n"
"ofwidth = %d\n",
input_f,
input_m,
output_f,
output_m,
irr_opt,
rr_opt,
cirr,
input_pixelformat,
output_pixelformat,
input_frame_width,
output_frame_width
);
if (equal_stripes) {
if ((irr_opt > cirr) /* overlap in the input is not requested */
&& ((input_frame_width % (input_m << 1)) == 0)
&& ((input_frame_width % (input_f << 1)) == 0)
&& ((output_frame_width % (output_m << 1)) == 0)
&& ((output_frame_width % (output_f << 1)) == 0)) {
/* without overlap */
left->input_width = right->input_width = right->input_column =
input_frame_width >> 1;
left->output_width = right->output_width = right->output_column =
output_frame_width >> 1;
left->input_column = 0;
left->output_column = 0;
div = div_u64(((((u64)irr_steps) << 32) *
(right->input_width - 1)), (right->output_width - 1));
left->irr = right->irr = truncate(0, div, 1);
} else { /* with overlap */
onw = truncate(0, (((u64)output_frame_width - 1) << 32) >> 1,
output_f);
inw = truncate(0, onw * irr_opt, input_f);
/* this is the maximal inw which allows the same resizing ratio */
/* in both stripes */
onw = truncate(1, (inw * rr_opt), output_f);
div = div_u64((((u64)(irr_steps * inw)) <<
32), onw);
left->irr = right->irr = truncate(0, div, 1);
left->output_width = right->output_width =
output_frame_width - onw;
/* These are valid assignments for output_width, */
/* assuming output_f is a multiple of output_m */
div = (((u64)(left->output_width-1) * (left->irr)) << 32);
div = (((u64)1) << 32) + div_u64(div, irr_steps);
left->input_width = right->input_width = truncate(1, div, input_m);
div = div_u64((((u64)((right->output_width - 1) * right->irr)) <<
32), irr_steps);
difwr = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
div = div_u64((difwr + (((u64)input_f) << 32)), 2);
left->input_column = truncate(0, div, input_f);
/* This splits the truncated input columns evenly */
/* between the left and right margins */
right->input_column = left->input_column + inw;
left->output_column = 0;
right->output_column = onw;
}
if (left->input_width > left->output_width) {
if (calc_split_resize_coeffs(left->input_width,
left->output_width,
&resize_coeff,
&downsize_coeff) < 0)
return -EINVAL;
if (downsize_coeff > 0) {
left->irr = right->irr =
(downsize_coeff << 14) | resize_coeff;
}
}
pr_debug("inw %d, onw %d, ilw %d, ilc %d, olw %d,"
" irw %d, irc %d, orw %d, orc %d, "
"difwr %llu, lirr %u\n",
inw, onw, left->input_width,
left->input_column, left->output_width,
right->input_width, right->input_column,
right->output_width,
right->output_column, difwr, left->irr);
} else { /* independent stripes */
onw_min = output_frame_width - maximal_stripe_width;
/* onw is a multiple of output_f, in the range */
/* [max(output_f,output_frame_width-maximal_stripe_width),*/
/*min(output_frame_width-2,maximal_stripe_width)] */
/* definitely beyond the cost of any valid setting */
cost_min = (((u64)input_frame_width) << 32) + cr;
onw = truncate(0, ((u64)maximal_stripe_width), output_f);
if (output_frame_width - onw == 1)
onw -= output_f; /* => onw and output_frame_width-1-onw are positive */
inw = truncate(0, onw * irr_opt, input_f);
/* this is the maximal inw which allows the same resizing ratio */
/* in both stripes */
onw = truncate(1, inw * rr_opt, output_f);
do {
div = div_u64((((u64)(irr_steps * inw)) << 32), onw);
left->irr = truncate(0, div, 1);
div = div_u64((((u64)(onw * left->irr)) << 32),
irr_steps);
dinw = (((u64)inw) << 32) - div;
div = div_u64((((u64)((output_frame_width - 1 - onw) * left->irr)) <<
32), irr_steps);
difwl = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
cost = difwl + (((u64)(cr * dinw)) >> 32);
if (cost < cost_min) {
inw_best = inw;
cost_min = cost;
}
inw -= input_f;
onw = truncate(1, inw * rr_opt, output_f);
/* This is the minimal onw which allows the same resizing ratio */
/* in both stripes */
} while (onw >= onw_min);
inw = inw_best;
onw = truncate(1, inw * rr_opt, output_f);
div = div_u64((((u64)(irr_steps * inw)) << 32), onw);
left->irr = truncate(0, div, 1);
left->output_width = onw;
right->output_width = output_frame_width - onw;
/* These are valid assignments for output_width, */
/* assuming output_f is a multiple of output_m */
left->input_width = truncate(1, ((u64)(inw + 1)) << 32, input_m);
right->input_width = truncate(1, ((u64)(input_frame_width - inw)) <<
32, input_m);
div = div_u64((((u64)(irr_steps * (input_frame_width - 1 - inw))) <<
32), (right->output_width - 1));
right->irr = truncate(0, div, 1);
temp = truncate(0, ((u64)left->irr) * ((((u64)1) << 32) + dirr), 1);
if (temp < right->irr)
right->irr = temp;
div = div_u64(((u64)((right->output_width - 1) * right->irr) <<
32), irr_steps);
difwr = (u64)(input_frame_width - 1 - inw) - div;
div = div_u64((difwr + (((u64)input_f) << 32)), 2);
left->input_column = truncate(0, div, input_f);
/* This splits the truncated input columns evenly */
/* between the left and right margins */
right->input_column = left->input_column + inw;
left->output_column = 0;
right->output_column = onw;
if (left->input_width > left->output_width) {
if (calc_split_resize_coeffs(left->input_width,
left->output_width,
&resize_coeff,
&downsize_coeff) < 0)
return -EINVAL;
left->irr = (downsize_coeff << 14) | resize_coeff;
}
if (right->input_width > right->output_width) {
if (calc_split_resize_coeffs(right->input_width,
right->output_width,
&resize_coeff,
&downsize_coeff) < 0)
return -EINVAL;
right->irr = (downsize_coeff << 14) | resize_coeff;
}
}
return status;
}
EXPORT_SYMBOL(ipu_calc_stripes_sizes);

View File

@ -0,0 +1,816 @@
/*
* Copyright 2008-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_capture.c
*
* @brief IPU capture dase functions
*
* @ingroup IPU
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ipu-v3.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include "ipu_prv.h"
#include "ipu_regs.h"
/*!
* _ipu_csi_mclk_set
*
* @param ipu ipu handler
* @param pixel_clk desired pixel clock frequency in Hz
* @param csi csi 0 or csi 1
*
* @return Returns 0 on success or negative error code on fail
*/
int _ipu_csi_mclk_set(struct ipu_soc *ipu, uint32_t pixel_clk, uint32_t csi)
{
uint32_t temp;
uint32_t div_ratio;
div_ratio = (clk_get_rate(ipu->ipu_clk) / pixel_clk) - 1;
if (div_ratio > 0xFF || div_ratio < 0) {
dev_dbg(ipu->dev, "value of pixel_clk extends normal range\n");
return -EINVAL;
}
temp = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
ipu_csi_write(ipu, csi, temp |
(div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
CSI_SENS_CONF);
return 0;
}
/*!
* ipu_csi_init_interface
* Sets initial values for the CSI registers.
* The width and height of the sensor and the actual frame size will be
* set to the same values.
* @param ipu ipu handler
* @param width Sensor width
* @param height Sensor height
* @param pixel_fmt pixel format
* @param cfg_param ipu_csi_signal_cfg_t structure
* @param csi csi 0 or csi 1
*
* @return 0 for success, -EINVAL for error
*/
int32_t
ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height,
uint32_t pixel_fmt, ipu_csi_signal_cfg_t cfg_param)
{
uint32_t data = 0;
uint32_t csi = cfg_param.csi;
/* Set SENS_DATA_FORMAT bits (8, 9 and 10)
RGB or YUV444 is 0 which is current value in data so not set
explicitly
This is also the default value if attempts are made to set it to
something invalid. */
switch (pixel_fmt) {
case IPU_PIX_FMT_YUYV:
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
break;
case IPU_PIX_FMT_UYVY:
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
break;
case IPU_PIX_FMT_RGB24:
case IPU_PIX_FMT_BGR24:
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
break;
case IPU_PIX_FMT_GENERIC:
case IPU_PIX_FMT_GENERIC_16:
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
break;
case IPU_PIX_FMT_RGB565:
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
break;
case IPU_PIX_FMT_RGB555:
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
break;
default:
return -EINVAL;
}
/* Set the CSI_SENS_CONF register remaining fields */
data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
_ipu_get(ipu);
mutex_lock(&ipu->mutex_lock);
ipu_csi_write(ipu, csi, data, CSI_SENS_CONF);
/* Setup sensor frame size */
ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE);
/* Set CCIR registers */
if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) {
ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
} else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) {
if (width == 720 && height == 625) {
/* PAL case */
/*
* Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
* Field0ActiveEnd = 0x4, Field0ActiveStart = 0
*/
ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1);
/*
* Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
* Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
*/
ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2);
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
} else if (width == 720 && height == 525) {
/* NTSC case */
/*
* Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
* Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
*/
ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_1);
/*
* Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
* Field1ActiveEnd = 0x4, Field1ActiveStart = 0
*/
ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2);
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
} else {
dev_err(ipu->dev, "Unsupported CCIR656 interlaced "
"video mode\n");
mutex_unlock(&ipu->mutex_lock);
_ipu_put(ipu);
return -EINVAL;
}
_ipu_csi_ccir_err_detection_enable(ipu, csi);
} else if ((cfg_param.clk_mode ==
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) ||
(cfg_param.clk_mode ==
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) ||
(cfg_param.clk_mode ==
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) ||
(cfg_param.clk_mode ==
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) {
ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
_ipu_csi_ccir_err_detection_enable(ipu, csi);
} else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) ||
(cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) {
_ipu_csi_ccir_err_detection_disable(ipu, csi);
}
dev_dbg(ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
ipu_csi_read(ipu, csi, CSI_SENS_CONF));
dev_dbg(ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE));
mutex_unlock(&ipu->mutex_lock);
_ipu_put(ipu);
return 0;
}
EXPORT_SYMBOL(ipu_csi_init_interface);
/*!
* ipu_csi_get_sensor_protocol
*
* @param ipu ipu handler
* @param csi csi 0 or csi 1
*
* @return Returns sensor protocol
*/
int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi)
{
int ret;
_ipu_get(ipu);
ret = (ipu_csi_read(ipu, csi, CSI_SENS_CONF) &
CSI_SENS_CONF_SENS_PRTCL_MASK) >>
CSI_SENS_CONF_SENS_PRTCL_SHIFT;
_ipu_put(ipu);
return ret;
}
EXPORT_SYMBOL(ipu_csi_get_sensor_protocol);
/*!
* ipu_csi_enable_mclk
*
* @param ipu ipu handler
* @param csi csi 0 or csi 1
* @param flag true to enable mclk, false to disable mclk
* @param wait true to wait 100ms make clock stable, false not wait
*
* @return Returns 0 on success
*/
int ipu_csi_enable_mclk(struct ipu_soc *ipu, int csi, bool flag, bool wait)
{
/* Return immediately if there is no csi_clk to manage */
if (ipu->csi_clk[csi] == NULL)
return 0;
if (flag) {
clk_enable(ipu->csi_clk[csi]);
if (wait == true)
msleep(10);
} else {
clk_disable(ipu->csi_clk[csi]);
}
return 0;
}
EXPORT_SYMBOL(ipu_csi_enable_mclk);
/*!
* ipu_csi_get_window_size
*
* @param ipu ipu handler
* @param width pointer to window width
* @param height pointer to window height
* @param csi csi 0 or csi 1
*/
void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t *height, uint32_t csi)
{
uint32_t reg;
_ipu_get(ipu);
mutex_lock(&ipu->mutex_lock);
reg = ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE);
*width = (reg & 0xFFFF) + 1;
*height = (reg >> 16 & 0xFFFF) + 1;
mutex_unlock(&ipu->mutex_lock);
_ipu_put(ipu);
}
EXPORT_SYMBOL(ipu_csi_get_window_size);
/*!
* ipu_csi_set_window_size
*
* @param ipu ipu handler
* @param width window width
* @param height window height
* @param csi csi 0 or csi 1
*/
void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi)
{
_ipu_get(ipu);
mutex_lock(&ipu->mutex_lock);
ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE);
mutex_unlock(&ipu->mutex_lock);
_ipu_put(ipu);
}
EXPORT_SYMBOL(ipu_csi_set_window_size);
/*!
* ipu_csi_set_window_pos
*
* @param ipu ipu handler
* @param left uint32 window x start
* @param top uint32 window y start
* @param csi csi 0 or csi 1
*/
void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi)
{
uint32_t temp;
_ipu_get(ipu);
mutex_lock(&ipu->mutex_lock);
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT));
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
mutex_unlock(&ipu->mutex_lock);
_ipu_put(ipu);
}
EXPORT_SYMBOL(ipu_csi_set_window_pos);
/*!
* _ipu_csi_horizontal_downsize_enable
* Enable horizontal downsizing(decimation) by 2.
*
* @param ipu ipu handler
* @param csi csi 0 or csi 1
*/
void _ipu_csi_horizontal_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
{
uint32_t temp;
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
temp |= CSI_HORI_DOWNSIZE_EN;
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
}
/*!
* _ipu_csi_horizontal_downsize_disable
* Disable horizontal downsizing(decimation) by 2.
*
* @param ipu ipu handler
* @param csi csi 0 or csi 1
*/
void _ipu_csi_horizontal_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
{
uint32_t temp;
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
temp &= ~CSI_HORI_DOWNSIZE_EN;
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
}
/*!
* _ipu_csi_vertical_downsize_enable
* Enable vertical downsizing(decimation) by 2.
*
* @param ipu ipu handler
* @param csi csi 0 or csi 1
*/
void _ipu_csi_vertical_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
{
uint32_t temp;
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
temp |= CSI_VERT_DOWNSIZE_EN;
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
}
/*!
* _ipu_csi_vertical_downsize_disable
* Disable vertical downsizing(decimation) by 2.
*
* @param ipu ipu handler
* @param csi csi 0 or csi 1
*/
void _ipu_csi_vertical_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
{
uint32_t temp;
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
temp &= ~CSI_VERT_DOWNSIZE_EN;
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
}
/*!
* _ipu_csi_set_test_generator
*
* @param ipu ipu handler
* @param active 1 for active and 0 for inactive
* @param r_value red value for the generated pattern of even pixel
* @param g_value green value for the generated pattern of even
* pixel
* @param b_value blue value for the generated pattern of even pixel
* @param pixel_clk desired pixel clock frequency in Hz
* @param csi csi 0 or csi 1
*/
void _ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value,
uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi)
{
uint32_t temp;
temp = ipu_csi_read(ipu, csi, CSI_TST_CTRL);
if (active == false) {
temp &= ~CSI_TEST_GEN_MODE_EN;
ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
} else {
/* Set sensb_mclk div_ratio*/
_ipu_csi_mclk_set(ipu, pix_clk, csi);
temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
CSI_TEST_GEN_B_MASK);
temp |= CSI_TEST_GEN_MODE_EN;
temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
(g_value << CSI_TEST_GEN_G_SHIFT) |
(b_value << CSI_TEST_GEN_B_SHIFT);
ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
}
}
/*!
* _ipu_csi_ccir_err_detection_en
* Enable error detection and correction for
* CCIR interlaced mode with protection bit.
*
* @param ipu ipu handler
* @param csi csi 0 or csi 1
*/
void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi)
{
uint32_t temp;
temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
temp |= CSI_CCIR_ERR_DET_EN;
ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
}
/*!
* _ipu_csi_ccir_err_detection_disable
* Disable error detection and correction for
* CCIR interlaced mode with protection bit.
*
* @param ipu ipu handler
* @param csi csi 0 or csi 1
*/
void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi)
{
uint32_t temp;
temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
temp &= ~CSI_CCIR_ERR_DET_EN;
ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
}
/*!
* _ipu_csi_set_mipi_di
*
* @param ipu ipu handler
* @param num MIPI data identifier 0-3 handled by CSI
* @param di_val data identifier value
* @param csi csi 0 or csi 1
*
* @return Returns 0 on success or negative error code on fail
*/
int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi)
{
uint32_t temp;
int retval = 0;
if (di_val > 0xFFL) {
retval = -EINVAL;
goto err;
}
temp = ipu_csi_read(ipu, csi, CSI_MIPI_DI);
switch (num) {
case IPU_CSI_MIPI_DI0:
temp &= ~CSI_MIPI_DI0_MASK;
temp |= (di_val << CSI_MIPI_DI0_SHIFT);
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
break;
case IPU_CSI_MIPI_DI1:
temp &= ~CSI_MIPI_DI1_MASK;
temp |= (di_val << CSI_MIPI_DI1_SHIFT);
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
break;
case IPU_CSI_MIPI_DI2:
temp &= ~CSI_MIPI_DI2_MASK;
temp |= (di_val << CSI_MIPI_DI2_SHIFT);
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
break;
case IPU_CSI_MIPI_DI3:
temp &= ~CSI_MIPI_DI3_MASK;
temp |= (di_val << CSI_MIPI_DI3_SHIFT);
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
break;
default:
retval = -EINVAL;
}
err:
return retval;
}
/*!
* _ipu_csi_set_skip_isp
*
* @param ipu ipu handler
* @param skip select frames to be skipped and set the
* correspond bits to 1
* @param max_ratio number of frames in a skipping set and the
* maximum value of max_ratio is 5
* @param csi csi 0 or csi 1
*
* @return Returns 0 on success or negative error code on fail
*/
int _ipu_csi_set_skip_isp(struct ipu_soc *ipu, uint32_t skip, uint32_t max_ratio, uint32_t csi)
{
uint32_t temp;
int retval = 0;
if (max_ratio > 5) {
retval = -EINVAL;
goto err;
}
temp = ipu_csi_read(ipu, csi, CSI_SKIP);
temp &= ~(CSI_MAX_RATIO_SKIP_ISP_MASK | CSI_SKIP_ISP_MASK);
temp |= (max_ratio << CSI_MAX_RATIO_SKIP_ISP_SHIFT) |
(skip << CSI_SKIP_ISP_SHIFT);
ipu_csi_write(ipu, csi, temp, CSI_SKIP);
err:
return retval;
}
/*!
* _ipu_csi_set_skip_smfc
*
* @param ipu ipu handler
* @param skip select frames to be skipped and set the
* correspond bits to 1
* @param max_ratio number of frames in a skipping set and the
* maximum value of max_ratio is 5
* @param id csi to smfc skipping id
* @param csi csi 0 or csi 1
*
* @return Returns 0 on success or negative error code on fail
*/
int _ipu_csi_set_skip_smfc(struct ipu_soc *ipu, uint32_t skip,
uint32_t max_ratio, uint32_t id, uint32_t csi)
{
uint32_t temp;
int retval = 0;
if (max_ratio > 5 || id > 3) {
retval = -EINVAL;
goto err;
}
temp = ipu_csi_read(ipu, csi, CSI_SKIP);
temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
CSI_SKIP_SMFC_MASK);
temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
(id << CSI_ID_2_SKIP_SHIFT) |
(skip << CSI_SKIP_SMFC_SHIFT);
ipu_csi_write(ipu, csi, temp, CSI_SKIP);
err:
return retval;
}
/*!
* _ipu_smfc_init
* Map CSI frames to IDMAC channels.
*
* @param ipu ipu handler
* @param channel IDMAC channel 0-3
* @param mipi_id mipi id number 0-3
* @param csi csi0 or csi1
*/
void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi)
{
uint32_t temp;
temp = ipu_smfc_read(ipu, SMFC_MAP);
switch (channel) {
case CSI_MEM0:
temp &= ~SMFC_MAP_CH0_MASK;
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT;
break;
case CSI_MEM1:
temp &= ~SMFC_MAP_CH1_MASK;
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT;
break;
case CSI_MEM2:
temp &= ~SMFC_MAP_CH2_MASK;
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT;
break;
case CSI_MEM3:
temp &= ~SMFC_MAP_CH3_MASK;
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT;
break;
default:
return;
}
ipu_smfc_write(ipu, temp, SMFC_MAP);
}
/*!
* _ipu_smfc_set_wmc
* Caution: The number of required channels, the enabled channels
* and the FIFO size per channel are configured restrictedly.
*
* @param ipu ipu handler
* @param channel IDMAC channel 0-3
* @param set set 1 or clear 0
* @param level water mark level when FIFO is on the
* relative size
*/
void _ipu_smfc_set_wmc(struct ipu_soc *ipu, ipu_channel_t channel, bool set, uint32_t level)
{
uint32_t temp;
temp = ipu_smfc_read(ipu, SMFC_WMC);
switch (channel) {
case CSI_MEM0:
if (set == true) {
temp &= ~SMFC_WM0_SET_MASK;
temp |= level << SMFC_WM0_SET_SHIFT;
} else {
temp &= ~SMFC_WM0_CLR_MASK;
temp |= level << SMFC_WM0_CLR_SHIFT;
}
break;
case CSI_MEM1:
if (set == true) {
temp &= ~SMFC_WM1_SET_MASK;
temp |= level << SMFC_WM1_SET_SHIFT;
} else {
temp &= ~SMFC_WM1_CLR_MASK;
temp |= level << SMFC_WM1_CLR_SHIFT;
}
break;
case CSI_MEM2:
if (set == true) {
temp &= ~SMFC_WM2_SET_MASK;
temp |= level << SMFC_WM2_SET_SHIFT;
} else {
temp &= ~SMFC_WM2_CLR_MASK;
temp |= level << SMFC_WM2_CLR_SHIFT;
}
break;
case CSI_MEM3:
if (set == true) {
temp &= ~SMFC_WM3_SET_MASK;
temp |= level << SMFC_WM3_SET_SHIFT;
} else {
temp &= ~SMFC_WM3_CLR_MASK;
temp |= level << SMFC_WM3_CLR_SHIFT;
}
break;
default:
return;
}
ipu_smfc_write(ipu, temp, SMFC_WMC);
}
/*!
* _ipu_smfc_set_burst_size
*
* @param ipu ipu handler
* @param channel IDMAC channel 0-3
* @param bs burst size of IDMAC channel,
* the value programmed here shoud be BURST_SIZE-1
*/
void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs)
{
uint32_t temp;
temp = ipu_smfc_read(ipu, SMFC_BS);
switch (channel) {
case CSI_MEM0:
temp &= ~SMFC_BS0_MASK;
temp |= bs << SMFC_BS0_SHIFT;
break;
case CSI_MEM1:
temp &= ~SMFC_BS1_MASK;
temp |= bs << SMFC_BS1_SHIFT;
break;
case CSI_MEM2:
temp &= ~SMFC_BS2_MASK;
temp |= bs << SMFC_BS2_SHIFT;
break;
case CSI_MEM3:
temp &= ~SMFC_BS3_MASK;
temp |= bs << SMFC_BS3_SHIFT;
break;
default:
return;
}
ipu_smfc_write(ipu, temp, SMFC_BS);
}
/*!
* _ipu_csi_init
*
* @param ipu ipu handler
* @param channel IDMAC channel
* @param csi csi 0 or csi 1
*
* @return Returns 0 on success or negative error code on fail
*/
int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi)
{
uint32_t csi_sens_conf, csi_dest;
int retval = 0;
switch (channel) {
case CSI_MEM0:
case CSI_MEM1:
case CSI_MEM2:
case CSI_MEM3:
csi_dest = CSI_DATA_DEST_IDMAC;
break;
case CSI_PRP_ENC_MEM:
case CSI_PRP_VF_MEM:
csi_dest = CSI_DATA_DEST_IC;
break;
default:
retval = -EINVAL;
goto err;
}
csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
ipu_csi_write(ipu, csi, csi_sens_conf | (csi_dest <<
CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF);
err:
return retval;
}
/*!
* csi_irq_handler
*
* @param irq interrupt id
* @param dev_id pointer to ipu handler
*
* @return Returns if irq is handled
*/
static irqreturn_t csi_irq_handler(int irq, void *dev_id)
{
struct ipu_soc *ipu = dev_id;
struct completion *comp = &ipu->csi_comp;
complete(comp);
return IRQ_HANDLED;
}
/*!
* _ipu_csi_wait4eof
*
* @param ipu ipu handler
* @param channel IDMAC channel
*
*/
void _ipu_csi_wait4eof(struct ipu_soc *ipu, ipu_channel_t channel)
{
int ret;
int irq = 0;
if (channel == CSI_MEM0)
irq = IPU_IRQ_CSI0_OUT_EOF;
else if (channel == CSI_MEM1)
irq = IPU_IRQ_CSI1_OUT_EOF;
else if (channel == CSI_MEM2)
irq = IPU_IRQ_CSI2_OUT_EOF;
else if (channel == CSI_MEM3)
irq = IPU_IRQ_CSI3_OUT_EOF;
else if (channel == CSI_PRP_ENC_MEM)
irq = IPU_IRQ_PRP_ENC_OUT_EOF;
else if (channel == CSI_PRP_VF_MEM)
irq = IPU_IRQ_PRP_VF_OUT_EOF;
else{
dev_err(ipu->dev, "Not a CSI channel\n");
return;
}
init_completion(&ipu->csi_comp);
ret = ipu_request_irq(ipu, irq, csi_irq_handler, 0, NULL, ipu);
if (ret < 0) {
dev_err(ipu->dev, "CSI irq %d in use\n", irq);
return;
}
ret = wait_for_completion_timeout(&ipu->csi_comp, msecs_to_jiffies(500));
ipu_free_irq(ipu, irq, ipu);
dev_dbg(ipu->dev, "CSI stop timeout - %d * 10ms\n", 5 - ret);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,924 @@
/*
* Copyright 2005-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*
* @file ipu_ic.c
*
* @brief IPU IC functions
*
* @ingroup IPU
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ipu-v3.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include "ipu_param_mem.h"
#include "ipu_regs.h"
enum {
IC_TASK_VIEWFINDER,
IC_TASK_ENCODER,
IC_TASK_POST_PROCESSOR
};
static void _init_csc(struct ipu_soc *ipu, uint8_t ic_task, ipu_color_space_t in_format,
ipu_color_space_t out_format, int csc_index);
static int _calc_resize_coeffs(struct ipu_soc *ipu,
uint32_t inSize, uint32_t outSize,
uint32_t *resizeCoeff,
uint32_t *downsizeCoeff);
void _ipu_vdi_set_top_field_man(struct ipu_soc *ipu, bool top_field_0)
{
uint32_t reg;
reg = ipu_vdi_read(ipu, VDI_C);
if (top_field_0)
reg &= ~VDI_C_TOP_FIELD_MAN_1;
else
reg |= VDI_C_TOP_FIELD_MAN_1;
ipu_vdi_write(ipu, reg, VDI_C);
}
void _ipu_vdi_set_motion(struct ipu_soc *ipu, ipu_motion_sel motion_sel)
{
uint32_t reg;
reg = ipu_vdi_read(ipu, VDI_C);
reg &= ~(VDI_C_MOT_SEL_FULL | VDI_C_MOT_SEL_MED | VDI_C_MOT_SEL_LOW);
if (motion_sel == HIGH_MOTION)
reg |= VDI_C_MOT_SEL_FULL;
else if (motion_sel == MED_MOTION)
reg |= VDI_C_MOT_SEL_MED;
else
reg |= VDI_C_MOT_SEL_LOW;
ipu_vdi_write(ipu, reg, VDI_C);
dev_dbg(ipu->dev, "VDI_C = \t0x%08X\n", reg);
}
void ic_dump_register(struct ipu_soc *ipu)
{
printk(KERN_DEBUG "IC_CONF = \t0x%08X\n", ipu_ic_read(ipu, IC_CONF));
printk(KERN_DEBUG "IC_PRP_ENC_RSC = \t0x%08X\n",
ipu_ic_read(ipu, IC_PRP_ENC_RSC));
printk(KERN_DEBUG "IC_PRP_VF_RSC = \t0x%08X\n",
ipu_ic_read(ipu, IC_PRP_VF_RSC));
printk(KERN_DEBUG "IC_PP_RSC = \t0x%08X\n", ipu_ic_read(ipu, IC_PP_RSC));
printk(KERN_DEBUG "IC_IDMAC_1 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_1));
printk(KERN_DEBUG "IC_IDMAC_2 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_2));
printk(KERN_DEBUG "IC_IDMAC_3 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_3));
}
void _ipu_ic_enable_task(struct ipu_soc *ipu, ipu_channel_t channel)
{
uint32_t ic_conf;
ic_conf = ipu_ic_read(ipu, IC_CONF);
switch (channel) {
case CSI_PRP_VF_MEM:
case MEM_PRP_VF_MEM:
ic_conf |= IC_CONF_PRPVF_EN;
break;
case MEM_VDI_PRP_VF_MEM:
ic_conf |= IC_CONF_PRPVF_EN;
break;
case MEM_VDI_MEM:
ic_conf |= IC_CONF_PRPVF_EN | IC_CONF_RWS_EN ;
break;
case MEM_ROT_VF_MEM:
ic_conf |= IC_CONF_PRPVF_ROT_EN;
break;
case CSI_PRP_ENC_MEM:
case MEM_PRP_ENC_MEM:
ic_conf |= IC_CONF_PRPENC_EN;
break;
case MEM_ROT_ENC_MEM:
ic_conf |= IC_CONF_PRPENC_ROT_EN;
break;
case MEM_PP_MEM:
ic_conf |= IC_CONF_PP_EN;
break;
case MEM_ROT_PP_MEM:
ic_conf |= IC_CONF_PP_ROT_EN;
break;
default:
break;
}
ipu_ic_write(ipu, ic_conf, IC_CONF);
}
void _ipu_ic_disable_task(struct ipu_soc *ipu, ipu_channel_t channel)
{
uint32_t ic_conf;
ic_conf = ipu_ic_read(ipu, IC_CONF);
switch (channel) {
case CSI_PRP_VF_MEM:
case MEM_PRP_VF_MEM:
ic_conf &= ~IC_CONF_PRPVF_EN;
break;
case MEM_VDI_PRP_VF_MEM:
ic_conf &= ~IC_CONF_PRPVF_EN;
break;
case MEM_VDI_MEM:
ic_conf &= ~(IC_CONF_PRPVF_EN | IC_CONF_RWS_EN);
break;
case MEM_ROT_VF_MEM:
ic_conf &= ~IC_CONF_PRPVF_ROT_EN;
break;
case CSI_PRP_ENC_MEM:
case MEM_PRP_ENC_MEM:
ic_conf &= ~IC_CONF_PRPENC_EN;
break;
case MEM_ROT_ENC_MEM:
ic_conf &= ~IC_CONF_PRPENC_ROT_EN;
break;
case MEM_PP_MEM:
ic_conf &= ~IC_CONF_PP_EN;
break;
case MEM_ROT_PP_MEM:
ic_conf &= ~IC_CONF_PP_ROT_EN;
break;
default:
break;
}
ipu_ic_write(ipu, ic_conf, IC_CONF);
}
void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params)
{
uint32_t reg;
uint32_t pixel_fmt;
uint32_t pix_per_burst;
reg = ((params->mem_prp_vf_mem.in_height-1) << 16) |
(params->mem_prp_vf_mem.in_width-1);
ipu_vdi_write(ipu, reg, VDI_FSIZE);
/* Full motion, only vertical filter is used
Burst size is 4 accesses */
if (params->mem_prp_vf_mem.in_pixel_fmt ==
IPU_PIX_FMT_UYVY ||
params->mem_prp_vf_mem.in_pixel_fmt ==
IPU_PIX_FMT_YUYV) {
pixel_fmt = VDI_C_CH_422;
pix_per_burst = 32;
} else {
pixel_fmt = VDI_C_CH_420;
pix_per_burst = 64;
}
reg = ipu_vdi_read(ipu, VDI_C);
reg |= pixel_fmt;
switch (channel) {
case MEM_VDI_PRP_VF_MEM:
reg |= VDI_C_BURST_SIZE2_4;
break;
case MEM_VDI_PRP_VF_MEM_P:
reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_SET_1 | VDI_C_VWM1_CLR_2;
break;
case MEM_VDI_PRP_VF_MEM_N:
reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_SET_1 | VDI_C_VWM3_CLR_2;
break;
case MEM_VDI_MEM:
reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK)
<< VDI_C_BURST_SIZE2_OFFSET;
break;
case MEM_VDI_MEM_P:
reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK)
<< VDI_C_BURST_SIZE1_OFFSET;
reg |= VDI_C_VWM1_SET_2 | VDI_C_VWM1_CLR_2;
break;
case MEM_VDI_MEM_N:
reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK)
<< VDI_C_BURST_SIZE3_OFFSET;
reg |= VDI_C_VWM3_SET_2 | VDI_C_VWM3_CLR_2;
break;
default:
break;
}
ipu_vdi_write(ipu, reg, VDI_C);
if (params->mem_prp_vf_mem.field_fmt == IPU_DEINTERLACE_FIELD_TOP)
_ipu_vdi_set_top_field_man(ipu, true);
else if (params->mem_prp_vf_mem.field_fmt == IPU_DEINTERLACE_FIELD_BOTTOM)
_ipu_vdi_set_top_field_man(ipu, false);
_ipu_vdi_set_motion(ipu, params->mem_prp_vf_mem.motion_sel);
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~IC_CONF_RWS_EN;
ipu_ic_write(ipu, reg, IC_CONF);
}
void _ipu_vdi_uninit(struct ipu_soc *ipu)
{
ipu_vdi_write(ipu, 0, VDI_FSIZE);
ipu_vdi_write(ipu, 0, VDI_C);
}
int _ipu_ic_init_prpvf(struct ipu_soc *ipu, ipu_channel_params_t *params,
bool src_is_csi)
{
uint32_t reg, ic_conf;
uint32_t downsizeCoeff, resizeCoeff;
ipu_color_space_t in_fmt, out_fmt;
int ret = 0;
/* Setup vertical resizing */
if (!params->mem_prp_vf_mem.outv_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_prp_vf_mem.in_height,
params->mem_prp_vf_mem.out_height,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate prpvf height "
"scaling coefficients\n");
return ret;
}
reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
} else
reg = (params->mem_prp_vf_mem.outv_resize_ratio) << 16;
/* Setup horizontal resizing */
if (!params->mem_prp_vf_mem.outh_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_prp_vf_mem.in_width,
params->mem_prp_vf_mem.out_width,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate prpvf width "
"scaling coefficients\n");
return ret;
}
reg |= (downsizeCoeff << 14) | resizeCoeff;
} else
reg |= params->mem_prp_vf_mem.outh_resize_ratio;
ipu_ic_write(ipu, reg, IC_PRP_VF_RSC);
ic_conf = ipu_ic_read(ipu, IC_CONF);
/* Setup color space conversion */
in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC1 */
_init_csc(ipu, IC_TASK_VIEWFINDER, RGB, out_fmt, 1);
ic_conf |= IC_CONF_PRPVF_CSC1;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC1 */
_init_csc(ipu, IC_TASK_VIEWFINDER, YCbCr, RGB, 1);
ic_conf |= IC_CONF_PRPVF_CSC1;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (params->mem_prp_vf_mem.graphics_combine_en) {
ic_conf |= IC_CONF_PRPVF_CMB;
if (!(ic_conf & IC_CONF_PRPVF_CSC1)) {
/* need transparent CSC1 conversion */
_init_csc(ipu, IC_TASK_VIEWFINDER, RGB, RGB, 1);
ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable RGB->RGB CSC */
}
in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_g_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC2 */
_init_csc(ipu, IC_TASK_VIEWFINDER, RGB, out_fmt, 2);
ic_conf |= IC_CONF_PRPVF_CSC2;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC2 */
_init_csc(ipu, IC_TASK_VIEWFINDER, YCbCr, RGB, 2);
ic_conf |= IC_CONF_PRPVF_CSC2;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (params->mem_prp_vf_mem.global_alpha_en) {
ic_conf |= IC_CONF_IC_GLB_LOC_A;
reg = ipu_ic_read(ipu, IC_CMBP_1);
reg &= ~(0xff);
reg |= params->mem_prp_vf_mem.alpha;
ipu_ic_write(ipu, reg, IC_CMBP_1);
} else
ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
if (params->mem_prp_vf_mem.key_color_en) {
ic_conf |= IC_CONF_KEY_COLOR_EN;
ipu_ic_write(ipu, params->mem_prp_vf_mem.key_color,
IC_CMBP_2);
} else
ic_conf &= ~IC_CONF_KEY_COLOR_EN;
} else {
ic_conf &= ~IC_CONF_PRPVF_CMB;
}
if (src_is_csi)
ic_conf &= ~IC_CONF_RWS_EN;
else
ic_conf |= IC_CONF_RWS_EN;
ipu_ic_write(ipu, ic_conf, IC_CONF);
return ret;
}
void _ipu_ic_uninit_prpvf(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~(IC_CONF_PRPVF_EN | IC_CONF_PRPVF_CMB |
IC_CONF_PRPVF_CSC2 | IC_CONF_PRPVF_CSC1);
ipu_ic_write(ipu, reg, IC_CONF);
}
void _ipu_ic_init_rotate_vf(struct ipu_soc *ipu, ipu_channel_params_t *params)
{
}
void _ipu_ic_uninit_rotate_vf(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~IC_CONF_PRPVF_ROT_EN;
ipu_ic_write(ipu, reg, IC_CONF);
}
int _ipu_ic_init_prpenc(struct ipu_soc *ipu, ipu_channel_params_t *params,
bool src_is_csi)
{
uint32_t reg, ic_conf;
uint32_t downsizeCoeff, resizeCoeff;
ipu_color_space_t in_fmt, out_fmt;
int ret = 0;
/* Setup vertical resizing */
if (!params->mem_prp_enc_mem.outv_resize_ratio) {
ret = _calc_resize_coeffs(ipu,
params->mem_prp_enc_mem.in_height,
params->mem_prp_enc_mem.out_height,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate prpenc height "
"scaling coefficients\n");
return ret;
}
reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
} else
reg = (params->mem_prp_enc_mem.outv_resize_ratio) << 16;
/* Setup horizontal resizing */
if (!params->mem_prp_enc_mem.outh_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_prp_enc_mem.in_width,
params->mem_prp_enc_mem.out_width,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate prpenc width "
"scaling coefficients\n");
return ret;
}
reg |= (downsizeCoeff << 14) | resizeCoeff;
} else
reg |= params->mem_prp_enc_mem.outh_resize_ratio;
ipu_ic_write(ipu, reg, IC_PRP_ENC_RSC);
ic_conf = ipu_ic_read(ipu, IC_CONF);
/* Setup color space conversion */
in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC1 */
_init_csc(ipu, IC_TASK_ENCODER, RGB, out_fmt, 1);
ic_conf |= IC_CONF_PRPENC_CSC1;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC1 */
_init_csc(ipu, IC_TASK_ENCODER, YCbCr, RGB, 1);
ic_conf |= IC_CONF_PRPENC_CSC1;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (src_is_csi)
ic_conf &= ~IC_CONF_RWS_EN;
else
ic_conf |= IC_CONF_RWS_EN;
ipu_ic_write(ipu, ic_conf, IC_CONF);
return ret;
}
void _ipu_ic_uninit_prpenc(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1);
ipu_ic_write(ipu, reg, IC_CONF);
}
void _ipu_ic_init_rotate_enc(struct ipu_soc *ipu, ipu_channel_params_t *params)
{
}
void _ipu_ic_uninit_rotate_enc(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~(IC_CONF_PRPENC_ROT_EN);
ipu_ic_write(ipu, reg, IC_CONF);
}
int _ipu_ic_init_pp(struct ipu_soc *ipu, ipu_channel_params_t *params)
{
uint32_t reg, ic_conf;
uint32_t downsizeCoeff, resizeCoeff;
ipu_color_space_t in_fmt, out_fmt;
int ret = 0;
/* Setup vertical resizing */
if (!params->mem_pp_mem.outv_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_pp_mem.in_height,
params->mem_pp_mem.out_height,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate pp height "
"scaling coefficients\n");
return ret;
}
reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
} else {
reg = (params->mem_pp_mem.outv_resize_ratio) << 16;
}
/* Setup horizontal resizing */
if (!params->mem_pp_mem.outh_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_pp_mem.in_width,
params->mem_pp_mem.out_width,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate pp width "
"scaling coefficients\n");
return ret;
}
reg |= (downsizeCoeff << 14) | resizeCoeff;
} else {
reg |= params->mem_pp_mem.outh_resize_ratio;
}
ipu_ic_write(ipu, reg, IC_PP_RSC);
ic_conf = ipu_ic_read(ipu, IC_CONF);
/* Setup color space conversion */
in_fmt = format_to_colorspace(params->mem_pp_mem.in_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC1 */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, out_fmt, 1);
ic_conf |= IC_CONF_PP_CSC1;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC1 */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, YCbCr, RGB, 1);
ic_conf |= IC_CONF_PP_CSC1;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (params->mem_pp_mem.graphics_combine_en) {
ic_conf |= IC_CONF_PP_CMB;
if (!(ic_conf & IC_CONF_PP_CSC1)) {
/* need transparent CSC1 conversion */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, RGB, 1);
ic_conf |= IC_CONF_PP_CSC1; /* Enable RGB->RGB CSC */
}
in_fmt = format_to_colorspace(params->mem_pp_mem.in_g_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC2 */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, out_fmt, 2);
ic_conf |= IC_CONF_PP_CSC2;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC2 */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, YCbCr, RGB, 2);
ic_conf |= IC_CONF_PP_CSC2;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (params->mem_pp_mem.global_alpha_en) {
ic_conf |= IC_CONF_IC_GLB_LOC_A;
reg = ipu_ic_read(ipu, IC_CMBP_1);
reg &= ~(0xff00);
reg |= (params->mem_pp_mem.alpha << 8);
ipu_ic_write(ipu, reg, IC_CMBP_1);
} else
ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
if (params->mem_pp_mem.key_color_en) {
ic_conf |= IC_CONF_KEY_COLOR_EN;
ipu_ic_write(ipu, params->mem_pp_mem.key_color,
IC_CMBP_2);
} else
ic_conf &= ~IC_CONF_KEY_COLOR_EN;
} else {
ic_conf &= ~IC_CONF_PP_CMB;
}
ipu_ic_write(ipu, ic_conf, IC_CONF);
return ret;
}
void _ipu_ic_uninit_pp(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~(IC_CONF_PP_EN | IC_CONF_PP_CSC1 | IC_CONF_PP_CSC2 |
IC_CONF_PP_CMB);
ipu_ic_write(ipu, reg, IC_CONF);
}
void _ipu_ic_init_rotate_pp(struct ipu_soc *ipu, ipu_channel_params_t *params)
{
}
void _ipu_ic_uninit_rotate_pp(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~IC_CONF_PP_ROT_EN;
ipu_ic_write(ipu, reg, IC_CONF);
}
int _ipu_ic_idma_init(struct ipu_soc *ipu, int dma_chan,
uint16_t width, uint16_t height,
int burst_size, ipu_rotate_mode_t rot)
{
u32 ic_idmac_1, ic_idmac_2, ic_idmac_3;
u32 temp_rot = bitrev8(rot) >> 5;
bool need_hor_flip = false;
if ((burst_size != 8) && (burst_size != 16)) {
dev_dbg(ipu->dev, "Illegal burst length for IC\n");
return -EINVAL;
}
width--;
height--;
if (temp_rot & 0x2) /* Need horizontal flip */
need_hor_flip = true;
ic_idmac_1 = ipu_ic_read(ipu, IC_IDMAC_1);
ic_idmac_2 = ipu_ic_read(ipu, IC_IDMAC_2);
ic_idmac_3 = ipu_ic_read(ipu, IC_IDMAC_3);
if (dma_chan == 22) { /* PP output - CB2 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16;
if (need_hor_flip)
ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS;
else
ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS;
ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK;
ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET;
ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK;
ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET;
} else if (dma_chan == 11) { /* PP Input - CB5 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16;
} else if (dma_chan == 47) { /* PP Rot input */
ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK;
ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET;
}
if (dma_chan == 12) { /* PRP Input - CB6 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16;
}
if (dma_chan == 20) { /* PRP ENC output - CB0 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16;
if (need_hor_flip)
ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS;
else
ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS;
ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK;
ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET;
ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK;
ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET;
} else if (dma_chan == 45) { /* PRP ENC Rot input */
ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK;
ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET;
}
if (dma_chan == 21) { /* PRP VF output - CB1 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16;
if (need_hor_flip)
ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS;
else
ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS;
ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK;
ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET;
ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK;
ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET;
} else if (dma_chan == 46) { /* PRP VF Rot input */
ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK;
ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET;
}
if (dma_chan == 14) { /* PRP VF graphics combining input - CB3 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16;
} else if (dma_chan == 15) { /* PP graphics combining input - CB4 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16;
} else if (dma_chan == 5) { /* VDIC OUTPUT - CB7 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16;
}
ipu_ic_write(ipu, ic_idmac_1, IC_IDMAC_1);
ipu_ic_write(ipu, ic_idmac_2, IC_IDMAC_2);
ipu_ic_write(ipu, ic_idmac_3, IC_IDMAC_3);
return 0;
}
static void _init_csc(struct ipu_soc *ipu, uint8_t ic_task, ipu_color_space_t in_format,
ipu_color_space_t out_format, int csc_index)
{
/*
* Y = 0.257 * R + 0.504 * G + 0.098 * B + 16;
* U = -0.148 * R - 0.291 * G + 0.439 * B + 128;
* V = 0.439 * R - 0.368 * G - 0.071 * B + 128;
*/
static const uint32_t rgb2ycbcr_coeff[4][3] = {
{0x0042, 0x0081, 0x0019},
{0x01DA, 0x01B6, 0x0070},
{0x0070, 0x01A2, 0x01EE},
{0x0040, 0x0200, 0x0200}, /* A0, A1, A2 */
};
/* transparent RGB->RGB matrix for combining
*/
static const uint32_t rgb2rgb_coeff[4][3] = {
{0x0080, 0x0000, 0x0000},
{0x0000, 0x0080, 0x0000},
{0x0000, 0x0000, 0x0080},
{0x0000, 0x0000, 0x0000}, /* A0, A1, A2 */
};
/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
static const uint32_t ycbcr2rgb_coeff[4][3] = {
{149, 0, 204},
{149, 462, 408},
{149, 255, 0},
{8192 - 446, 266, 8192 - 554}, /* A0, A1, A2 */
};
uint32_t param;
uint32_t *base = NULL;
if (ic_task == IC_TASK_ENCODER) {
base = (uint32_t *)ipu->tpmem_base + 0x2008 / 4;
} else if (ic_task == IC_TASK_VIEWFINDER) {
if (csc_index == 1)
base = (uint32_t *)ipu->tpmem_base + 0x4028 / 4;
else
base = (uint32_t *)ipu->tpmem_base + 0x4040 / 4;
} else if (ic_task == IC_TASK_POST_PROCESSOR) {
if (csc_index == 1)
base = (uint32_t *)ipu->tpmem_base + 0x6060 / 4;
else
base = (uint32_t *)ipu->tpmem_base + 0x6078 / 4;
} else {
BUG();
}
if ((in_format == YCbCr) && (out_format == RGB)) {
/* Init CSC (YCbCr->RGB) */
param = (ycbcr2rgb_coeff[3][0] << 27) |
(ycbcr2rgb_coeff[0][0] << 18) |
(ycbcr2rgb_coeff[1][1] << 9) | ycbcr2rgb_coeff[2][2];
writel(param, base++);
/* scale = 2, sat = 0 */
param = (ycbcr2rgb_coeff[3][0] >> 5) | (2L << (40 - 32));
writel(param, base++);
param = (ycbcr2rgb_coeff[3][1] << 27) |
(ycbcr2rgb_coeff[0][1] << 18) |
(ycbcr2rgb_coeff[1][0] << 9) | ycbcr2rgb_coeff[2][0];
writel(param, base++);
param = (ycbcr2rgb_coeff[3][1] >> 5);
writel(param, base++);
param = (ycbcr2rgb_coeff[3][2] << 27) |
(ycbcr2rgb_coeff[0][2] << 18) |
(ycbcr2rgb_coeff[1][2] << 9) | ycbcr2rgb_coeff[2][1];
writel(param, base++);
param = (ycbcr2rgb_coeff[3][2] >> 5);
writel(param, base++);
} else if ((in_format == RGB) && (out_format == YCbCr)) {
/* Init CSC (RGB->YCbCr) */
param = (rgb2ycbcr_coeff[3][0] << 27) |
(rgb2ycbcr_coeff[0][0] << 18) |
(rgb2ycbcr_coeff[1][1] << 9) | rgb2ycbcr_coeff[2][2];
writel(param, base++);
/* scale = 1, sat = 0 */
param = (rgb2ycbcr_coeff[3][0] >> 5) | (1UL << 8);
writel(param, base++);
param = (rgb2ycbcr_coeff[3][1] << 27) |
(rgb2ycbcr_coeff[0][1] << 18) |
(rgb2ycbcr_coeff[1][0] << 9) | rgb2ycbcr_coeff[2][0];
writel(param, base++);
param = (rgb2ycbcr_coeff[3][1] >> 5);
writel(param, base++);
param = (rgb2ycbcr_coeff[3][2] << 27) |
(rgb2ycbcr_coeff[0][2] << 18) |
(rgb2ycbcr_coeff[1][2] << 9) | rgb2ycbcr_coeff[2][1];
writel(param, base++);
param = (rgb2ycbcr_coeff[3][2] >> 5);
writel(param, base++);
} else if ((in_format == RGB) && (out_format == RGB)) {
/* Init CSC */
param =
(rgb2rgb_coeff[3][0] << 27) | (rgb2rgb_coeff[0][0] << 18) |
(rgb2rgb_coeff[1][1] << 9) | rgb2rgb_coeff[2][2];
writel(param, base++);
/* scale = 2, sat = 0 */
param = (rgb2rgb_coeff[3][0] >> 5) | (2UL << 8);
writel(param, base++);
param =
(rgb2rgb_coeff[3][1] << 27) | (rgb2rgb_coeff[0][1] << 18) |
(rgb2rgb_coeff[1][0] << 9) | rgb2rgb_coeff[2][0];
writel(param, base++);
param = (rgb2rgb_coeff[3][1] >> 5);
writel(param, base++);
param =
(rgb2rgb_coeff[3][2] << 27) | (rgb2rgb_coeff[0][2] << 18) |
(rgb2rgb_coeff[1][2] << 9) | rgb2rgb_coeff[2][1];
writel(param, base++);
param = (rgb2rgb_coeff[3][2] >> 5);
writel(param, base++);
} else {
dev_err(ipu->dev, "Unsupported color space conversion\n");
}
}
static int _calc_resize_coeffs(struct ipu_soc *ipu,
uint32_t inSize, uint32_t outSize,
uint32_t *resizeCoeff,
uint32_t *downsizeCoeff)
{
uint32_t tempSize;
uint32_t tempDownsize;
if (inSize > 4096) {
dev_err(ipu->dev, "IC input size(%d) cannot exceed 4096\n",
inSize);
return -EINVAL;
}
if (outSize > 1024) {
dev_err(ipu->dev, "IC output size(%d) cannot exceed 1024\n",
outSize);
return -EINVAL;
}
if ((outSize << 3) < inSize) {
dev_err(ipu->dev, "IC cannot downsize more than 8:1\n");
return -EINVAL;
}
/* Compute downsizing coefficient */
/* Output of downsizing unit cannot be more than 1024 */
tempDownsize = 0;
tempSize = inSize;
while (((tempSize > 1024) || (tempSize >= outSize * 2)) &&
(tempDownsize < 2)) {
tempSize >>= 1;
tempDownsize++;
}
*downsizeCoeff = tempDownsize;
/* compute resizing coefficient using the following equation:
resizeCoeff = M*(SI -1)/(SO - 1)
where M = 2^13, SI - input size, SO - output size */
*resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
if (*resizeCoeff >= 16384L) {
dev_err(ipu->dev, "Overflow on IC resize coefficient.\n");
return -EINVAL;
}
dev_dbg(ipu->dev, "resizing from %u -> %u pixels, "
"downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
*downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);
return 0;
}
void _ipu_vdi_toggle_top_field_man(struct ipu_soc *ipu)
{
uint32_t reg;
uint32_t mask_reg;
reg = ipu_vdi_read(ipu, VDI_C);
mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
reg &= ~VDI_C_TOP_FIELD_MAN_1;
else
reg |= VDI_C_TOP_FIELD_MAN_1;
ipu_vdi_write(ipu, reg, VDI_C);
}

View File

@ -0,0 +1,996 @@
/*
* Copyright 2005-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __INCLUDE_IPU_PARAM_MEM_H__
#define __INCLUDE_IPU_PARAM_MEM_H__
#include <linux/bitrev.h>
#include <linux/types.h>
#include "ipu_prv.h"
extern u32 *ipu_cpmem_base;
struct ipu_ch_param_word {
uint32_t data[5];
uint32_t res[3];
};
struct ipu_ch_param {
struct ipu_ch_param_word word[2];
};
#define ipu_ch_param_addr(ipu, ch) (((struct ipu_ch_param *)ipu->cpmem_base) + (ch))
#define _param_word(base, w) \
(((struct ipu_ch_param *)(base))->word[(w)].data)
#define ipu_ch_param_set_field(base, w, bit, size, v) { \
int i = (bit) / 32; \
int off = (bit) % 32; \
_param_word(base, w)[i] |= (v) << off; \
if (((bit)+(size)-1)/32 > i) { \
_param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \
} \
}
#define ipu_ch_param_set_field_io(base, w, bit, size, v) { \
int i = (bit) / 32; \
int off = (bit) % 32; \
unsigned reg_offset; \
u32 temp; \
reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \
reg_offset += i; \
temp = readl((u32 *)base + reg_offset); \
temp |= (v) << off; \
writel(temp, (u32 *)base + reg_offset); \
if (((bit)+(size)-1)/32 > i) { \
reg_offset++; \
temp = readl((u32 *)base + reg_offset); \
temp |= (v) >> (off ? (32 - off) : 0); \
writel(temp, (u32 *)base + reg_offset); \
} \
}
#define ipu_ch_param_mod_field(base, w, bit, size, v) { \
int i = (bit) / 32; \
int off = (bit) % 32; \
u32 mask = (1UL << size) - 1; \
u32 temp = _param_word(base, w)[i]; \
temp &= ~(mask << off); \
_param_word(base, w)[i] = temp | (v) << off; \
if (((bit)+(size)-1)/32 > i) { \
temp = _param_word(base, w)[i + 1]; \
temp &= ~(mask >> (32 - off)); \
_param_word(base, w)[i + 1] = \
temp | ((v) >> (off ? (32 - off) : 0)); \
} \
}
#define ipu_ch_param_mod_field_io(base, w, bit, size, v) { \
int i = (bit) / 32; \
int off = (bit) % 32; \
u32 mask = (1UL << size) - 1; \
unsigned reg_offset; \
u32 temp; \
reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \
reg_offset += i; \
temp = readl((u32 *)base + reg_offset); \
temp &= ~(mask << off); \
temp |= (v) << off; \
writel(temp, (u32 *)base + reg_offset); \
if (((bit)+(size)-1)/32 > i) { \
reg_offset++; \
temp = readl((u32 *)base + reg_offset); \
temp &= ~(mask >> (32 - off)); \
temp |= ((v) >> (off ? (32 - off) : 0)); \
writel(temp, (u32 *)base + reg_offset); \
} \
}
#define ipu_ch_param_read_field(base, w, bit, size) ({ \
u32 temp2; \
int i = (bit) / 32; \
int off = (bit) % 32; \
u32 mask = (1UL << size) - 1; \
u32 temp1 = _param_word(base, w)[i]; \
temp1 = mask & (temp1 >> off); \
if (((bit)+(size)-1)/32 > i) { \
temp2 = _param_word(base, w)[i + 1]; \
temp2 &= mask >> (off ? (32 - off) : 0); \
temp1 |= temp2 << (off ? (32 - off) : 0); \
} \
temp1; \
})
#define ipu_ch_param_read_field_io(base, w, bit, size) ({ \
u32 temp1, temp2; \
int i = (bit) / 32; \
int off = (bit) % 32; \
u32 mask = (1UL << size) - 1; \
unsigned reg_offset; \
reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \
reg_offset += i; \
temp1 = readl((u32 *)base + reg_offset); \
temp1 = mask & (temp1 >> off); \
if (((bit)+(size)-1)/32 > i) { \
reg_offset++; \
temp2 = readl((u32 *)base + reg_offset); \
temp2 &= mask >> (off ? (32 - off) : 0); \
temp1 |= temp2 << (off ? (32 - off) : 0); \
} \
temp1; \
})
static inline int __ipu_ch_get_third_buf_cpmem_num(int ch)
{
switch (ch) {
case 8:
return 64;
case 9:
return 65;
case 10:
return 66;
case 13:
return 67;
case 21:
return 68;
case 23:
return 69;
case 27:
return 70;
case 28:
return 71;
default:
return -EINVAL;
}
return 0;
}
static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p,
int red_width, int red_offset,
int green_width, int green_offset,
int blue_width, int blue_offset,
int alpha_width, int alpha_offset)
{
/* Setup red width and offset */
ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1);
ipu_ch_param_set_field(p, 1, 128, 5, red_offset);
/* Setup green width and offset */
ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1);
ipu_ch_param_set_field(p, 1, 133, 5, green_offset);
/* Setup blue width and offset */
ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1);
ipu_ch_param_set_field(p, 1, 138, 5, blue_offset);
/* Setup alpha width and offset */
ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1);
ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset);
}
static inline void _ipu_ch_param_dump(struct ipu_soc *ipu, int ch)
{
struct ipu_ch_param *p = ipu_ch_param_addr(ipu, ch);
dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", ch,
p->word[0].data[0], p->word[0].data[1], p->word[0].data[2],
p->word[0].data[3], p->word[0].data[4]);
dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", ch,
p->word[1].data[0], p->word[1].data[1], p->word[1].data[2],
p->word[1].data[3], p->word[1].data[4]);
dev_dbg(ipu->dev, "PFS 0x%x, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 85, 4));
dev_dbg(ipu->dev, "BPP 0x%x, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3));
dev_dbg(ipu->dev, "NPB 0x%x\n",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7));
dev_dbg(ipu->dev, "FW %d, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 125, 13));
dev_dbg(ipu->dev, "FH %d, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 138, 12));
dev_dbg(ipu->dev, "EBA0 0x%x\n",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 0, 29) << 3);
dev_dbg(ipu->dev, "EBA1 0x%x\n",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 29, 29) << 3);
dev_dbg(ipu->dev, "Stride %d\n",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14));
dev_dbg(ipu->dev, "scan_order %d\n",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1));
dev_dbg(ipu->dev, "uv_stride %d\n",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 128, 14));
dev_dbg(ipu->dev, "u_offset 0x%x\n",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22) << 3);
dev_dbg(ipu->dev, "v_offset 0x%x\n",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22) << 3);
dev_dbg(ipu->dev, "Width0 %d+1, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 116, 3));
dev_dbg(ipu->dev, "Width1 %d+1, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 119, 3));
dev_dbg(ipu->dev, "Width2 %d+1, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 122, 3));
dev_dbg(ipu->dev, "Width3 %d+1, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3));
dev_dbg(ipu->dev, "Offset0 %d, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 128, 5));
dev_dbg(ipu->dev, "Offset1 %d, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 133, 5));
dev_dbg(ipu->dev, "Offset2 %d, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 138, 5));
dev_dbg(ipu->dev, "Offset3 %d\n",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 143, 5));
}
static inline void fill_cpmem(struct ipu_soc *ipu, int ch, struct ipu_ch_param *params)
{
int i, w;
void *addr = ipu_ch_param_addr(ipu, ch);
/* 2 words, 5 valid data */
for (w = 0; w < 2; w++) {
for (i = 0; i < 5; i++) {
writel(params->word[w].data[i], addr);
addr += 4;
}
addr += 12;
}
}
static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch,
uint32_t pixel_fmt, uint32_t width,
uint32_t height, uint32_t stride,
uint32_t u, uint32_t v,
uint32_t uv_stride, dma_addr_t addr0,
dma_addr_t addr1, dma_addr_t addr2)
{
uint32_t u_offset = 0;
uint32_t v_offset = 0;
uint32_t bs = 0;
int32_t sub_ch = 0;
struct ipu_ch_param params;
memset(&params, 0, sizeof(params));
ipu_ch_param_set_field(&params, 0, 125, 13, width - 1);
if (((ch == 8) || (ch == 9) || (ch == 10)) && !ipu->vdoa_en) {
ipu_ch_param_set_field(&params, 0, 138, 12, (height / 2) - 1);
ipu_ch_param_set_field(&params, 1, 102, 14, (stride * 2) - 1);
} else {
/* note: for vdoa+vdi- ch8/9/10, always use band mode */
ipu_ch_param_set_field(&params, 0, 138, 12, height - 1);
ipu_ch_param_set_field(&params, 1, 102, 14, stride - 1);
}
/* EBA is 8-byte aligned */
ipu_ch_param_set_field(&params, 1, 0, 29, addr0 >> 3);
ipu_ch_param_set_field(&params, 1, 29, 29, addr1 >> 3);
if (addr0%8)
dev_warn(ipu->dev,
"IDMAC%d's EBA0 is not 8-byte aligned\n", ch);
if (addr1%8)
dev_warn(ipu->dev,
"IDMAC%d's EBA1 is not 8-byte aligned\n", ch);
switch (pixel_fmt) {
case IPU_PIX_FMT_GENERIC:
/*Represents 8-bit Generic data */
ipu_ch_param_set_field(&params, 0, 107, 3, 5); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 6); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 63); /* burst size */
break;
case IPU_PIX_FMT_GENERIC_16:
/* Represents 16-bit generic data */
ipu_ch_param_set_field(&params, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 6); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
break;
case IPU_PIX_FMT_GENERIC_32:
/*Represents 32-bit Generic data */
break;
case IPU_PIX_FMT_RGB565:
ipu_ch_param_set_field(&params, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
_ipu_ch_params_set_packing(&params, 5, 0, 6, 5, 5, 11, 8, 16);
break;
case IPU_PIX_FMT_BGRA4444:
ipu_ch_param_set_field(&params, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
_ipu_ch_params_set_packing(&params, 4, 4, 4, 8, 4, 12, 4, 0);
break;
case IPU_PIX_FMT_BGRA5551:
ipu_ch_param_set_field(&params, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
_ipu_ch_params_set_packing(&params, 5, 1, 5, 6, 5, 11, 1, 0);
break;
case IPU_PIX_FMT_BGR24:
ipu_ch_param_set_field(&params, 0, 107, 3, 1); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 19); /* burst size */
_ipu_ch_params_set_packing(&params, 8, 0, 8, 8, 8, 16, 8, 24);
break;
case IPU_PIX_FMT_RGB24:
case IPU_PIX_FMT_YUV444:
ipu_ch_param_set_field(&params, 0, 107, 3, 1); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 19); /* burst size */
_ipu_ch_params_set_packing(&params, 8, 16, 8, 8, 8, 0, 8, 24);
break;
case IPU_PIX_FMT_VYU444:
ipu_ch_param_set_field(&params, 0, 107, 3, 1); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 19); /* burst size */
_ipu_ch_params_set_packing(&params, 8, 8, 8, 0, 8, 16, 8, 24);
break;
case IPU_PIX_FMT_AYUV:
ipu_ch_param_set_field(&params, 0, 107, 3, 0); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
_ipu_ch_params_set_packing(&params, 8, 8, 8, 16, 8, 24, 8, 0);
break;
case IPU_PIX_FMT_BGRA32:
case IPU_PIX_FMT_BGR32:
ipu_ch_param_set_field(&params, 0, 107, 3, 0); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
_ipu_ch_params_set_packing(&params, 8, 8, 8, 16, 8, 24, 8, 0);
break;
case IPU_PIX_FMT_RGBA32:
case IPU_PIX_FMT_RGB32:
ipu_ch_param_set_field(&params, 0, 107, 3, 0); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
_ipu_ch_params_set_packing(&params, 8, 24, 8, 16, 8, 8, 8, 0);
break;
case IPU_PIX_FMT_ABGR32:
ipu_ch_param_set_field(&params, 0, 107, 3, 0); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
_ipu_ch_params_set_packing(&params, 8, 0, 8, 8, 8, 16, 8, 24);
break;
case IPU_PIX_FMT_UYVY:
ipu_ch_param_set_field(&params, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 0xA); /* pix format */
if ((ch == 8) || (ch == 9) || (ch == 10)) {
ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
} else {
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
}
break;
case IPU_PIX_FMT_YUYV:
ipu_ch_param_set_field(&params, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 0x8); /* pix format */
if ((ch == 8) || (ch == 9) || (ch == 10)) {
if (ipu->vdoa_en) {
ipu_ch_param_set_field(&params, 1, 78, 7, 31);
} else {
ipu_ch_param_set_field(&params, 1, 78, 7, 15);
}
} else {
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
}
break;
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
ipu_ch_param_set_field(&params, 1, 85, 4, 2); /* pix format */
if (uv_stride < stride / 2)
uv_stride = stride / 2;
u_offset = stride * height;
v_offset = u_offset + (uv_stride * height / 2);
if ((ch == 8) || (ch == 9) || (ch == 10)) {
ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
uv_stride = uv_stride*2;
} else {
if (_ipu_is_smfc_chan(ch) &&
ipu->smfc_idmac_12bit_3planar_bs_fixup)
bs = 15;
else
bs = 31;
ipu_ch_param_set_field(&params, 1, 78, 7, bs); /* burst size */
}
break;
case IPU_PIX_FMT_YVU420P:
ipu_ch_param_set_field(&params, 1, 85, 4, 2); /* pix format */
if (uv_stride < stride / 2)
uv_stride = stride / 2;
v_offset = stride * height;
u_offset = v_offset + (uv_stride * height / 2);
if ((ch == 8) || (ch == 9) || (ch == 10)) {
ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
uv_stride = uv_stride*2;
} else {
if (_ipu_is_smfc_chan(ch) &&
ipu->smfc_idmac_12bit_3planar_bs_fixup)
bs = 15;
else
bs = 31;
ipu_ch_param_set_field(&params, 1, 78, 7, bs); /* burst size */
}
break;
case IPU_PIX_FMT_YVU422P:
/* BPP & pixel format */
ipu_ch_param_set_field(&params, 1, 85, 4, 1); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
if (uv_stride < stride / 2)
uv_stride = stride / 2;
v_offset = (v == 0) ? stride * height : v;
u_offset = (u == 0) ? v_offset + v_offset / 2 : u;
break;
case IPU_PIX_FMT_YUV422P:
/* BPP & pixel format */
ipu_ch_param_set_field(&params, 1, 85, 4, 1); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
if (uv_stride < stride / 2)
uv_stride = stride / 2;
u_offset = (u == 0) ? stride * height : u;
v_offset = (v == 0) ? u_offset + u_offset / 2 : v;
break;
case IPU_PIX_FMT_YUV444P:
/* BPP & pixel format */
ipu_ch_param_set_field(&params, 1, 85, 4, 0); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
uv_stride = stride;
u_offset = (u == 0) ? stride * height : u;
v_offset = (v == 0) ? u_offset * 2 : v;
break;
case IPU_PIX_FMT_NV16:
ipu_ch_param_set_field(&params, 1, 85, 4, 1); /* pix format */
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
uv_stride = stride;
u_offset = (u == 0) ? stride * height : u;
break;
case IPU_PIX_FMT_NV12:
/* BPP & pixel format */
ipu_ch_param_set_field(&params, 1, 85, 4, 4); /* pix format */
uv_stride = stride;
u_offset = (u == 0) ? stride * height : u;
if ((ch == 8) || (ch == 9) || (ch == 10)) {
if (ipu->vdoa_en) {
/* one field buffer, memory width 64bits */
ipu_ch_param_set_field(&params, 1, 78, 7, 63);
} else {
ipu_ch_param_set_field(&params, 1, 78, 7, 15);
/* top/bottom field in one buffer*/
uv_stride = uv_stride*2;
}
} else {
ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
}
break;
default:
dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n");
break;
}
/*set burst size to 16*/
if (uv_stride)
ipu_ch_param_set_field(&params, 1, 128, 14, uv_stride - 1);
/* Get the uv offset from user when need cropping */
if (u || v) {
u_offset = u;
v_offset = v;
}
/* UBO and VBO are 22-bit and 8-byte aligned */
if (u_offset/8 > 0x3fffff)
dev_warn(ipu->dev,
"IDMAC%d's U offset exceeds IPU limitation\n", ch);
if (v_offset/8 > 0x3fffff)
dev_warn(ipu->dev,
"IDMAC%d's V offset exceeds IPU limitation\n", ch);
if (u_offset%8)
dev_warn(ipu->dev,
"IDMAC%d's U offset is not 8-byte aligned\n", ch);
if (v_offset%8)
dev_warn(ipu->dev,
"IDMAC%d's V offset is not 8-byte aligned\n", ch);
ipu_ch_param_set_field(&params, 0, 46, 22, u_offset / 8);
ipu_ch_param_set_field(&params, 0, 68, 22, v_offset / 8);
dev_dbg(ipu->dev, "initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ipu, ch));
fill_cpmem(ipu, ch, &params);
if (addr2) {
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_set_field(&params, 1, 0, 29, addr2 >> 3);
ipu_ch_param_set_field(&params, 1, 29, 29, 0);
if (addr2%8)
dev_warn(ipu->dev,
"IDMAC%d's sub-CPMEM entry%d EBA0 is not "
"8-byte aligned\n", ch, sub_ch);
dev_dbg(ipu->dev, "initializing idma ch %d @ %p sub cpmem\n", ch,
ipu_ch_param_addr(ipu, sub_ch));
fill_cpmem(ipu, sub_ch, &params);
}
};
static inline void _ipu_ch_param_set_burst_size(struct ipu_soc *ipu,
uint32_t ch,
uint16_t burst_pixels)
{
int32_t sub_ch = 0;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7,
burst_pixels - 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 78, 7,
burst_pixels - 1);
};
static inline int _ipu_ch_param_get_burst_size(struct ipu_soc *ipu, uint32_t ch)
{
return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7) + 1;
};
static inline int _ipu_ch_param_get_bpp(struct ipu_soc *ipu, uint32_t ch)
{
return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3);
};
static inline void _ipu_ch_param_set_buffer(struct ipu_soc *ipu, uint32_t ch,
int bufNum, dma_addr_t phyaddr)
{
if (bufNum == 2) {
ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (ch <= 0)
return;
bufNum = 0;
}
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 29 * bufNum, 29,
phyaddr / 8);
};
static inline void _ipu_ch_param_set_rotation(struct ipu_soc *ipu, uint32_t ch,
ipu_rotate_mode_t rot)
{
u32 temp_rot = bitrev8(rot) >> 5;
int32_t sub_ch = 0;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 119, 3, temp_rot);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 119, 3, temp_rot);
};
static inline void _ipu_ch_param_set_block_mode(struct ipu_soc *ipu, uint32_t ch)
{
int32_t sub_ch = 0;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 117, 2, 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 117, 2, 1);
};
static inline void _ipu_ch_param_set_alpha_use_separate_channel(struct ipu_soc *ipu,
uint32_t ch,
bool option)
{
int32_t sub_ch = 0;
if (option) {
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 1);
} else {
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 0);
}
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
if (option) {
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 1);
} else {
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 0);
}
};
static inline void _ipu_ch_param_set_alpha_condition_read(struct ipu_soc *ipu, uint32_t ch)
{
int32_t sub_ch = 0;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 149, 1, 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 149, 1, 1);
};
static inline void _ipu_ch_param_set_alpha_buffer_memory(struct ipu_soc *ipu, uint32_t ch)
{
int alp_mem_idx;
int32_t sub_ch = 0;
switch (ch) {
case 14: /* PRP graphic */
alp_mem_idx = 0;
break;
case 15: /* PP graphic */
alp_mem_idx = 1;
break;
case 23: /* DP BG SYNC graphic */
alp_mem_idx = 4;
break;
case 27: /* DP FG SYNC graphic */
alp_mem_idx = 2;
break;
default:
dev_err(ipu->dev, "unsupported correlative channel of local "
"alpha channel\n");
return;
}
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 90, 3, alp_mem_idx);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 90, 3, alp_mem_idx);
};
static inline void _ipu_ch_param_set_interlaced_scan(struct ipu_soc *ipu, uint32_t ch)
{
u32 stride;
int32_t sub_ch = 0;
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1, 1);
if (sub_ch > 0)
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 113, 1, 1);
stride = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14) + 1;
/* ILO is 20-bit and 8-byte aligned */
if (stride/8 > 0xfffff)
dev_warn(ipu->dev,
"IDMAC%d's ILO exceeds IPU limitation\n", ch);
if (stride%8)
dev_warn(ipu->dev,
"IDMAC%d's ILO is not 8-byte aligned\n", ch);
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 58, 20, stride / 8);
if (sub_ch > 0)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 58, 20,
stride / 8);
stride *= 2;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14, stride - 1);
if (sub_ch > 0)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 102, 14,
stride - 1);
};
static inline void _ipu_ch_param_set_axi_id(struct ipu_soc *ipu, uint32_t ch, uint32_t id)
{
int32_t sub_ch = 0;
id %= 4;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2, id);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 93, 2, id);
};
static inline int _ipu_ch_param_get_axi_id(struct ipu_soc *ipu, uint32_t ch)
{
return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2);
}
static inline int __ipu_ch_offset_calc(uint32_t pixel_fmt,
uint32_t width,
uint32_t height,
uint32_t stride,
uint32_t u,
uint32_t v,
uint32_t uv_stride,
uint32_t vertical_offset,
uint32_t horizontal_offset,
uint32_t *u_offset,
uint32_t *v_offset)
{
uint32_t u_fix = 0;
uint32_t v_fix = 0;
switch (pixel_fmt) {
case IPU_PIX_FMT_GENERIC:
case IPU_PIX_FMT_GENERIC_16:
case IPU_PIX_FMT_GENERIC_32:
case IPU_PIX_FMT_RGB565:
case IPU_PIX_FMT_BGR24:
case IPU_PIX_FMT_RGB24:
case IPU_PIX_FMT_YUV444:
case IPU_PIX_FMT_BGRA32:
case IPU_PIX_FMT_BGR32:
case IPU_PIX_FMT_RGBA32:
case IPU_PIX_FMT_RGB32:
case IPU_PIX_FMT_ABGR32:
case IPU_PIX_FMT_UYVY:
case IPU_PIX_FMT_YUYV:
case IPU_PIX_FMT_GPU32_SB_ST:
case IPU_PIX_FMT_GPU32_SB_SRT:
case IPU_PIX_FMT_GPU32_ST:
case IPU_PIX_FMT_GPU32_SRT:
case IPU_PIX_FMT_GPU16_SB_ST:
case IPU_PIX_FMT_GPU16_SB_SRT:
case IPU_PIX_FMT_GPU16_ST:
case IPU_PIX_FMT_GPU16_SRT:
*u_offset = 0;
*v_offset = 0;
break;
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
if (uv_stride < stride / 2)
uv_stride = stride / 2;
*u_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset / 2) +
horizontal_offset / 2;
*v_offset = *u_offset + (uv_stride * height / 2);
u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
(horizontal_offset / 2) -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
(horizontal_offset / 2) -
(stride * vertical_offset) - (horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_YVU420P:
if (uv_stride < stride / 2)
uv_stride = stride / 2;
*v_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset / 2) +
horizontal_offset / 2;
*u_offset = *v_offset + (uv_stride * height / 2);
u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
(horizontal_offset / 2) -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
(horizontal_offset / 2) -
(stride * vertical_offset) - (horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_YVU422P:
if (uv_stride < stride / 2)
uv_stride = stride / 2;
*v_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset) +
horizontal_offset / 2;
*u_offset = *v_offset + uv_stride * height;
u_fix = u ? (u + (uv_stride * vertical_offset) +
horizontal_offset / 2 -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset) +
horizontal_offset / 2 -
(stride * vertical_offset) - (horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_YUV422P:
if (uv_stride < stride / 2)
uv_stride = stride / 2;
*u_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset) +
horizontal_offset / 2;
*v_offset = *u_offset + uv_stride * height;
u_fix = u ? (u + (uv_stride * vertical_offset) +
horizontal_offset / 2 -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset) +
horizontal_offset / 2 -
(stride * vertical_offset) - (horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_YUV444P:
uv_stride = stride;
*u_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset) +
horizontal_offset;
*v_offset = *u_offset + uv_stride * height;
u_fix = u ? (u + (uv_stride * vertical_offset) +
horizontal_offset -
(stride * vertical_offset) -
(horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset) +
horizontal_offset -
(stride * vertical_offset) -
(horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_NV12:
case IPU_PIX_FMT_NV16:
case PRE_PIX_FMT_NV21:
case PRE_PIX_FMT_NV61:
uv_stride = stride;
*u_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset / 2) +
horizontal_offset;
*v_offset = 0;
u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
horizontal_offset -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
break;
default:
return -EINVAL;
}
if (u_fix > *u_offset)
*u_offset = u_fix;
if (v_fix > *v_offset)
*v_offset = v_fix;
return 0;
}
/* IDMAC U/V offset changing support */
/* U and V input is not affected, */
/* the update is done by new calculation according to */
/* vertical_offset and horizontal_offset */
static inline void _ipu_ch_offset_update(struct ipu_soc *ipu,
int ch,
uint32_t pixel_fmt,
uint32_t width,
uint32_t height,
uint32_t stride,
uint32_t u,
uint32_t v,
uint32_t uv_stride,
uint32_t vertical_offset,
uint32_t horizontal_offset)
{
uint32_t u_offset = 0;
uint32_t v_offset = 0;
uint32_t old_offset = 0;
int32_t sub_ch = 0;
int ret;
ret = __ipu_ch_offset_calc(pixel_fmt, width, height, stride,
u, v, uv_stride,
vertical_offset, horizontal_offset,
&u_offset, &v_offset);
if (ret) {
dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n");
return;
}
/* UBO and VBO are 22-bit and 8-byte aligned */
if (u_offset/8 > 0x3fffff)
dev_warn(ipu->dev,
"IDMAC%d's U offset exceeds IPU limitation\n", ch);
if (v_offset/8 > 0x3fffff)
dev_warn(ipu->dev,
"IDMAC%d's V offset exceeds IPU limitation\n", ch);
if (u_offset%8)
dev_warn(ipu->dev,
"IDMAC%d's U offset is not 8-byte aligned\n", ch);
if (v_offset%8)
dev_warn(ipu->dev,
"IDMAC%d's V offset is not 8-byte aligned\n", ch);
old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22);
if (old_offset != u_offset / 8)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22, u_offset / 8);
old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22);
if (old_offset != v_offset / 8)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22, v_offset / 8);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22);
if (old_offset != u_offset / 8)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22, u_offset / 8);
old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22);
if (old_offset != v_offset / 8)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22, v_offset / 8);
};
static inline void _ipu_ch_params_set_alpha_width(struct ipu_soc *ipu, uint32_t ch, int alpha_width)
{
int32_t sub_ch = 0;
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3, alpha_width - 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 125, 3, alpha_width - 1);
};
static inline void _ipu_ch_param_set_bandmode(struct ipu_soc *ipu,
uint32_t ch, uint32_t band_height)
{
int32_t sub_ch = 0;
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch),
0, 114, 3, band_height - 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch),
0, 114, 3, band_height - 1);
dev_dbg(ipu->dev, "BNDM 0x%x, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 114, 3));
}
/*
* The IPUv3 IDMAC has a bug to read 32bpp pixels from a graphics plane
* whose alpha component is at the most significant 8 bits. The bug only
* impacts on cases in which the relevant separate alpha channel is enabled.
*
* Return true on bad alpha component position, otherwise, return false.
*/
static inline bool _ipu_ch_param_bad_alpha_pos(uint32_t pixel_fmt)
{
switch (pixel_fmt) {
case IPU_PIX_FMT_BGRA32:
case IPU_PIX_FMT_BGR32:
case IPU_PIX_FMT_RGBA32:
case IPU_PIX_FMT_RGB32:
return true;
}
return false;
}
#endif

View File

@ -0,0 +1,317 @@
/*
* Copyright (C) 2013-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file ipu_pixel_clk.c
*
* @brief IPU pixel clock implementation
*
* @ingroup IPU
*/
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/ipu-v3.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "ipu_prv.h"
#include "ipu_regs.h"
/*
* muxd clock implementation
*/
struct clk_di_mux {
struct clk_hw hw;
u8 ipu_id;
u8 di_id;
u8 flags;
u8 index;
};
#define to_clk_di_mux(_hw) container_of(_hw, struct clk_di_mux, hw)
static int _ipu_pixel_clk_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_di_mux *mux = to_clk_di_mux(hw);
struct ipu_soc *ipu = ipu_get_soc(mux->ipu_id);
u32 di_gen;
di_gen = ipu_di_read(ipu, mux->di_id, DI_GENERAL);
if (index == 0)
/* ipu1_clk or ipu2_clk internal clk */
di_gen &= ~DI_GEN_DI_CLK_EXT;
else
di_gen |= DI_GEN_DI_CLK_EXT;
ipu_di_write(ipu, mux->di_id, di_gen, DI_GENERAL);
mux->index = index;
pr_debug("ipu_pixel_clk: di_clk_ext:0x%x, di_gen reg:0x%x.\n",
!(di_gen & DI_GEN_DI_CLK_EXT), di_gen);
return 0;
}
static u8 _ipu_pixel_clk_get_parent(struct clk_hw *hw)
{
struct clk_di_mux *mux = to_clk_di_mux(hw);
return mux->index;
}
const struct clk_ops clk_mux_di_ops = {
.get_parent = _ipu_pixel_clk_get_parent,
.set_parent = _ipu_pixel_clk_set_parent,
};
struct clk *clk_register_mux_pix_clk(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
u8 ipu_id, u8 di_id, u8 clk_mux_flags)
{
struct clk_di_mux *mux;
struct clk *clk;
struct clk_init_data init;
mux = kzalloc(sizeof(struct clk_di_mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &clk_mux_di_ops;
init.flags = flags;
init.parent_names = parent_names;
init.num_parents = num_parents;
mux->ipu_id = ipu_id;
mux->di_id = di_id;
mux->flags = clk_mux_flags | CLK_SET_RATE_PARENT;
mux->hw.init = &init;
clk = clk_register(dev, &mux->hw);
if (IS_ERR(clk))
kfree(mux);
return clk;
}
/*
* Gated clock implementation
*/
struct clk_di_div {
struct clk_hw hw;
u8 ipu_id;
u8 di_id;
u8 flags;
};
#define to_clk_di_div(_hw) container_of(_hw, struct clk_di_div, hw)
static unsigned long _ipu_pixel_clk_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_di_div *di_div = to_clk_di_div(hw);
struct ipu_soc *ipu = ipu_get_soc(di_div->ipu_id);
u32 div;
u64 final_rate = (unsigned long long)parent_rate * 16;
_ipu_get(ipu);
div = ipu_di_read(ipu, di_div->di_id, DI_BS_CLKGEN0);
_ipu_put(ipu);
pr_debug("ipu_di%d read BS_CLKGEN0 div:%d, final_rate:%lld, prate:%ld\n",
di_div->di_id, div, final_rate, parent_rate);
if (div == 0)
return 0;
do_div(final_rate, div);
return (unsigned long)final_rate;
}
static long _ipu_pixel_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_clk_rate)
{
u64 div, final_rate;
u32 remainder;
u64 parent_rate = (unsigned long long)(*parent_clk_rate) * 16;
/*
* Calculate divider
* Fractional part is 4 bits,
* so simply multiply by 2^4 to get fractional part.
*/
div = parent_rate;
remainder = do_div(div, rate);
/* Round the divider value */
if (remainder > (rate/2))
div++;
if (div < 0x10) /* Min DI disp clock divider is 1 */
div = 0x10;
if (div & ~0xFEF)
div &= 0xFF8;
else {
/* Round up divider if it gets us closer to desired pix clk */
if ((div & 0xC) == 0xC) {
div += 0x10;
div &= ~0xF;
}
}
final_rate = parent_rate;
do_div(final_rate, div);
return final_rate;
}
static int _ipu_pixel_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_clk_rate)
{
struct clk_di_div *di_div = to_clk_di_div(hw);
struct ipu_soc *ipu = ipu_get_soc(di_div->ipu_id);
u64 div, parent_rate;
u32 remainder;
parent_rate = (unsigned long long)parent_clk_rate * 16;
div = parent_rate;
remainder = do_div(div, rate);
/* Round the divider value */
if (remainder > (rate/2))
div++;
/* Round up divider if it gets us closer to desired pix clk */
if ((div & 0xC) == 0xC) {
div += 0x10;
div &= ~0xF;
}
if (div > 0x1000)
pr_err("Overflow, di:%d, DI_BS_CLKGEN0 div:0x%x\n",
di_div->di_id, (u32)div);
_ipu_get(ipu);
ipu_di_write(ipu, di_div->di_id, (u32)div, DI_BS_CLKGEN0);
/* Setup pixel clock timing */
/* FIXME: needs to be more flexible */
/* Down time is half of period */
ipu_di_write(ipu, di_div->di_id, ((u32)div / 16) << 16, DI_BS_CLKGEN1);
_ipu_put(ipu);
return 0;
}
static struct clk_ops clk_div_ops = {
.recalc_rate = _ipu_pixel_clk_div_recalc_rate,
.round_rate = _ipu_pixel_clk_div_round_rate,
.set_rate = _ipu_pixel_clk_div_set_rate,
};
struct clk *clk_register_div_pix_clk(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
u8 ipu_id, u8 di_id, u8 clk_div_flags)
{
struct clk_di_div *di_div;
struct clk *clk;
struct clk_init_data init;
di_div = kzalloc(sizeof(struct clk_di_div), GFP_KERNEL);
if (!di_div)
return ERR_PTR(-ENOMEM);
/* struct clk_di_div assignments */
di_div->ipu_id = ipu_id;
di_div->di_id = di_id;
di_div->flags = clk_div_flags;
init.name = name;
init.ops = &clk_div_ops;
init.flags = flags | CLK_SET_RATE_PARENT;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
di_div->hw.init = &init;
clk = clk_register(dev, &di_div->hw);
if (IS_ERR(clk))
kfree(clk);
return clk;
}
/*
* Gated clock implementation
*/
struct clk_di_gate {
struct clk_hw hw;
u8 ipu_id;
u8 di_id;
u8 flags;
};
#define to_clk_di_gate(_hw) container_of(_hw, struct clk_di_gate, hw)
static int _ipu_pixel_clk_enable(struct clk_hw *hw)
{
struct clk_di_gate *gate = to_clk_di_gate(hw);
struct ipu_soc *ipu = ipu_get_soc(gate->ipu_id);
u32 disp_gen;
disp_gen = ipu_cm_read(ipu, IPU_DISP_GEN);
disp_gen |= gate->di_id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE;
ipu_cm_write(ipu, disp_gen, IPU_DISP_GEN);
return 0;
}
static void _ipu_pixel_clk_disable(struct clk_hw *hw)
{
struct clk_di_gate *gate = to_clk_di_gate(hw);
struct ipu_soc *ipu = ipu_get_soc(gate->ipu_id);
u32 disp_gen;
disp_gen = ipu_cm_read(ipu, IPU_DISP_GEN);
disp_gen &= gate->di_id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
ipu_cm_write(ipu, disp_gen, IPU_DISP_GEN);
}
static struct clk_ops clk_gate_di_ops = {
.enable = _ipu_pixel_clk_enable,
.disable = _ipu_pixel_clk_disable,
};
struct clk *clk_register_gate_pix_clk(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
u8 ipu_id, u8 di_id, u8 clk_gate_flags)
{
struct clk_di_gate *gate;
struct clk *clk;
struct clk_init_data init;
gate = kzalloc(sizeof(struct clk_di_gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
gate->ipu_id = ipu_id;
gate->di_id = di_id;
gate->flags = clk_gate_flags;
init.name = name;
init.ops = &clk_gate_di_ops;
init.flags = flags | CLK_SET_RATE_PARENT;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
gate->hw.init = &init;
clk = clk_register(dev, &gate->hw);
if (IS_ERR(clk))
kfree(clk);
return clk;
}

View File

@ -0,0 +1,368 @@
/*
* Copyright 2005-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __INCLUDE_IPU_PRV_H__
#define __INCLUDE_IPU_PRV_H__
#include <linux/clkdev.h>
#include <linux/device.h>
#include <linux/fsl_devices.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#define MXC_IPU_MAX_NUM 2
#define MXC_DI_NUM_PER_IPU 2
/* Globals */
extern int dmfc_type_setup;
#define IDMA_CHAN_INVALID 0xFF
#define HIGH_RESOLUTION_WIDTH 1024
enum ipuv3_type {
IPUv3D, /* i.MX37 */
IPUv3EX, /* i.MX51 */
IPUv3M, /* i.MX53 */
IPUv3H, /* i.MX6Q/SDL */
};
#define IPU_MAX_VDI_IN_WIDTH(type) ({ (type) >= IPUv3M ? 968 : 720; })
struct ipu_irq_node {
irqreturn_t(*handler) (int, void *); /*!< the ISR */
const char *name; /*!< device associated with the interrupt */
void *dev_id; /*!< some unique information for the ISR */
__u32 flags; /*!< not used */
};
enum csc_type_t {
RGB2YUV = 0,
YUV2RGB,
RGB2RGB,
YUV2YUV,
CSC_NONE,
CSC_NUM
};
struct ipu_soc {
unsigned int id;
unsigned int devtype;
bool online;
/*clk*/
struct clk *ipu_clk;
struct clk *di_clk[2];
struct clk *di_clk_sel[2];
struct clk *pixel_clk[2];
bool pixel_clk_en[2];
struct clk *pixel_clk_sel[2];
struct clk *csi_clk[2];
struct clk *prg_clk;
/*irq*/
int irq_sync;
int irq_err;
struct ipu_irq_node irq_list[IPU_IRQ_COUNT];
/*reg*/
void __iomem *cm_reg;
void __iomem *idmac_reg;
void __iomem *dp_reg;
void __iomem *ic_reg;
void __iomem *dc_reg;
void __iomem *dc_tmpl_reg;
void __iomem *dmfc_reg;
void __iomem *di_reg[2];
void __iomem *smfc_reg;
void __iomem *csi_reg[2];
void __iomem *cpmem_base;
void __iomem *tpmem_base;
void __iomem *vdi_reg;
struct device *dev;
ipu_channel_t csi_channel[2];
ipu_channel_t using_ic_dirct_ch;
unsigned char dc_di_assignment[10];
bool sec_chan_en[24];
bool thrd_chan_en[24];
bool chan_is_interlaced[52];
uint32_t channel_init_mask;
uint32_t channel_enable_mask;
/*use count*/
int dc_use_count;
int dp_use_count;
int dmfc_use_count;
int smfc_use_count;
int ic_use_count;
int rot_use_count;
int vdi_use_count;
int di_use_count[2];
int csi_use_count[2];
struct mutex mutex_lock;
spinlock_t int_reg_spin_lock;
spinlock_t rdy_reg_spin_lock;
int dmfc_size_28;
int dmfc_size_29;
int dmfc_size_24;
int dmfc_size_27;
int dmfc_size_23;
enum csc_type_t fg_csc_type;
enum csc_type_t bg_csc_type;
bool color_key_4rgb;
bool dc_swap;
struct completion dc_comp;
struct completion csi_comp;
struct rot_mem {
void *vaddr;
dma_addr_t paddr;
int size;
} rot_dma[2];
int vdoa_en;
struct task_struct *thread[2];
/*
* Bypass reset to avoid display channel being
* stopped by probe since it may starts to work
* in bootloader.
*/
bool bypass_reset;
unsigned int ch0123_axi;
unsigned int ch23_axi;
unsigned int ch27_axi;
unsigned int ch28_axi;
unsigned int normal_axi;
bool smfc_idmac_12bit_3planar_bs_fixup; /* workaround little stripes */
};
struct ipu_channel {
u8 video_in_dma;
u8 alpha_in_dma;
u8 graph_in_dma;
u8 out_dma;
};
enum ipu_dmfc_type {
DMFC_NORMAL = 0,
DMFC_HIGH_RESOLUTION_DC,
DMFC_HIGH_RESOLUTION_DP,
DMFC_HIGH_RESOLUTION_ONLY_DP,
};
static inline int _ipu_is_smfc_chan(uint32_t dma_chan)
{
return ((dma_chan >= 0) && (dma_chan <= 3));
}
static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
{
return readl(ipu->cm_reg + offset);
}
static inline void ipu_cm_write(struct ipu_soc *ipu,
u32 value, unsigned offset)
{
writel(value, ipu->cm_reg + offset);
}
static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
{
return readl(ipu->idmac_reg + offset);
}
static inline void ipu_idmac_write(struct ipu_soc *ipu,
u32 value, unsigned offset)
{
writel(value, ipu->idmac_reg + offset);
}
static inline u32 ipu_dc_read(struct ipu_soc *ipu, unsigned offset)
{
return readl(ipu->dc_reg + offset);
}
static inline void ipu_dc_write(struct ipu_soc *ipu,
u32 value, unsigned offset)
{
writel(value, ipu->dc_reg + offset);
}
static inline u32 ipu_dc_tmpl_read(struct ipu_soc *ipu, unsigned offset)
{
return readl(ipu->dc_tmpl_reg + offset);
}
static inline void ipu_dc_tmpl_write(struct ipu_soc *ipu,
u32 value, unsigned offset)
{
writel(value, ipu->dc_tmpl_reg + offset);
}
static inline u32 ipu_dmfc_read(struct ipu_soc *ipu, unsigned offset)
{
return readl(ipu->dmfc_reg + offset);
}
static inline void ipu_dmfc_write(struct ipu_soc *ipu,
u32 value, unsigned offset)
{
writel(value, ipu->dmfc_reg + offset);
}
static inline u32 ipu_dp_read(struct ipu_soc *ipu, unsigned offset)
{
return readl(ipu->dp_reg + offset);
}
static inline void ipu_dp_write(struct ipu_soc *ipu,
u32 value, unsigned offset)
{
writel(value, ipu->dp_reg + offset);
}
static inline u32 ipu_di_read(struct ipu_soc *ipu, int di, unsigned offset)
{
return readl(ipu->di_reg[di] + offset);
}
static inline void ipu_di_write(struct ipu_soc *ipu, int di,
u32 value, unsigned offset)
{
writel(value, ipu->di_reg[di] + offset);
}
static inline u32 ipu_csi_read(struct ipu_soc *ipu, int csi, unsigned offset)
{
return readl(ipu->csi_reg[csi] + offset);
}
static inline void ipu_csi_write(struct ipu_soc *ipu, int csi,
u32 value, unsigned offset)
{
writel(value, ipu->csi_reg[csi] + offset);
}
static inline u32 ipu_smfc_read(struct ipu_soc *ipu, unsigned offset)
{
return readl(ipu->smfc_reg + offset);
}
static inline void ipu_smfc_write(struct ipu_soc *ipu,
u32 value, unsigned offset)
{
writel(value, ipu->smfc_reg + offset);
}
static inline u32 ipu_vdi_read(struct ipu_soc *ipu, unsigned offset)
{
return readl(ipu->vdi_reg + offset);
}
static inline void ipu_vdi_write(struct ipu_soc *ipu,
u32 value, unsigned offset)
{
writel(value, ipu->vdi_reg + offset);
}
static inline u32 ipu_ic_read(struct ipu_soc *ipu, unsigned offset)
{
return readl(ipu->ic_reg + offset);
}
static inline void ipu_ic_write(struct ipu_soc *ipu,
u32 value, unsigned offset)
{
writel(value, ipu->ic_reg + offset);
}
int register_ipu_device(struct ipu_soc *ipu, int id);
void unregister_ipu_device(struct ipu_soc *ipu, int id);
ipu_color_space_t format_to_colorspace(uint32_t fmt);
bool ipu_pixel_format_has_alpha(uint32_t fmt);
void ipu_dump_registers(struct ipu_soc *ipu);
uint32_t _ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel);
void ipu_disp_init(struct ipu_soc *ipu);
void _ipu_init_dc_mappings(struct ipu_soc *ipu);
int _ipu_dp_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t in_pixel_fmt,
uint32_t out_pixel_fmt);
void _ipu_dp_uninit(struct ipu_soc *ipu, ipu_channel_t channel);
void _ipu_dc_init(struct ipu_soc *ipu, int dc_chan, int di, bool interlaced, uint32_t pixel_fmt);
void _ipu_dc_uninit(struct ipu_soc *ipu, int dc_chan);
void _ipu_dp_dc_enable(struct ipu_soc *ipu, ipu_channel_t channel);
void _ipu_dp_dc_disable(struct ipu_soc *ipu, ipu_channel_t channel, bool swap);
void _ipu_dmfc_init(struct ipu_soc *ipu, int dmfc_type, int first);
void _ipu_dmfc_set_wait4eot(struct ipu_soc *ipu, int dma_chan, int width);
void _ipu_dmfc_set_burst_size(struct ipu_soc *ipu, int dma_chan, int burst_size);
int _ipu_disp_chan_is_interlaced(struct ipu_soc *ipu, ipu_channel_t channel);
void _ipu_ic_enable_task(struct ipu_soc *ipu, ipu_channel_t channel);
void _ipu_ic_disable_task(struct ipu_soc *ipu, ipu_channel_t channel);
int _ipu_ic_init_prpvf(struct ipu_soc *ipu, ipu_channel_params_t *params,
bool src_is_csi);
void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params);
void _ipu_vdi_uninit(struct ipu_soc *ipu);
void _ipu_ic_uninit_prpvf(struct ipu_soc *ipu);
void _ipu_ic_init_rotate_vf(struct ipu_soc *ipu, ipu_channel_params_t *params);
void _ipu_ic_uninit_rotate_vf(struct ipu_soc *ipu);
void _ipu_ic_init_csi(struct ipu_soc *ipu, ipu_channel_params_t *params);
void _ipu_ic_uninit_csi(struct ipu_soc *ipu);
int _ipu_ic_init_prpenc(struct ipu_soc *ipu, ipu_channel_params_t *params,
bool src_is_csi);
void _ipu_ic_uninit_prpenc(struct ipu_soc *ipu);
void _ipu_ic_init_rotate_enc(struct ipu_soc *ipu, ipu_channel_params_t *params);
void _ipu_ic_uninit_rotate_enc(struct ipu_soc *ipu);
int _ipu_ic_init_pp(struct ipu_soc *ipu, ipu_channel_params_t *params);
void _ipu_ic_uninit_pp(struct ipu_soc *ipu);
void _ipu_ic_init_rotate_pp(struct ipu_soc *ipu, ipu_channel_params_t *params);
void _ipu_ic_uninit_rotate_pp(struct ipu_soc *ipu);
int _ipu_ic_idma_init(struct ipu_soc *ipu, int dma_chan, uint16_t width, uint16_t height,
int burst_size, ipu_rotate_mode_t rot);
void _ipu_vdi_toggle_top_field_man(struct ipu_soc *ipu);
int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi);
int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi);
void ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value,
uint32_t g_value, uint32_t b_value,
uint32_t pix_clk, uint32_t csi);
void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi);
void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi);
void _ipu_csi_wait4eof(struct ipu_soc *ipu, ipu_channel_t channel);
void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi);
void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs);
void _ipu_dp_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3]);
int32_t _ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel,
int16_t x_pos, int16_t y_pos);
int32_t _ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel,
int16_t *x_pos, int16_t *y_pos);
void _ipu_get(struct ipu_soc *ipu);
void _ipu_put(struct ipu_soc *ipu);
struct clk *clk_register_mux_pix_clk(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
u8 ipu_id, u8 di_id, u8 clk_mux_flags);
struct clk *clk_register_div_pix_clk(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
u8 ipu_id, u8 di_id, u8 clk_div_flags);
struct clk *clk_register_gate_pix_clk(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
u8 ipu_id, u8 di_id, u8 clk_gate_flags);
#endif /* __INCLUDE_IPU_PRV_H__ */

View File

@ -0,0 +1,702 @@
/*
* Copyright (C) 2005-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*
* @file ipu_regs.h
*
* @brief IPU Register definitions
*
* @ingroup IPU
*/
#ifndef __IPU_REGS_INCLUDED__
#define __IPU_REGS_INCLUDED__
#include "ipu_prv.h"
#define IPU_MCU_T_DEFAULT 8
/* Register addresses */
/* IPU Common registers */
#define IPU_CM_REG(offset) (offset)
#define IPU_CONF IPU_CM_REG(0)
#define IPU_SRM_PRI1 IPU_CM_REG(0x00A0)
#define IPU_SRM_PRI2 IPU_CM_REG(0x00A4)
#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00A8)
#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00AC)
#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00B0)
#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00B4)
#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00B8)
#define IPU_SKIP IPU_CM_REG(0x00BC)
#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00C0)
#define IPU_DISP_GEN IPU_CM_REG(0x00C4)
#define IPU_DISP_ALT1 IPU_CM_REG(0x00C8)
#define IPU_DISP_ALT2 IPU_CM_REG(0x00CC)
#define IPU_DISP_ALT3 IPU_CM_REG(0x00D0)
#define IPU_DISP_ALT4 IPU_CM_REG(0x00D4)
#define IPU_SNOOP IPU_CM_REG(0x00D8)
#define IPU_MEM_RST IPU_CM_REG(0x00DC)
#define IPU_PM IPU_CM_REG(0x00E0)
#define IPU_GPR IPU_CM_REG(0x00E4)
#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32))
#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32))
/*
* IPUv3D doesn't support triple buffer, so point
* IPU_CHA_TRB_MODE_SEL, IPU_CHA_TRIPLE_CUR_BUF and
* IPU_CHA_BUF2_RDY to readonly
* IPU_ALT_CUR_BUF0 for IPUv3D.
*/
#define IPU_CHA_TRB_MODE_SEL(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0178 + 4 * ((ch) / 32)) : \
(0x012C); })
#define IPU_CHA_TRIPLE_CUR_BUF(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0258 + \
4 * (((ch) * 2) / 32)) : \
(0x012C); })
#define IPU_CHA_BUF2_RDY(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0288 + 4 * ((ch) / 32)) : \
(0x012C); })
#define IPU_CHA_CUR_BUF(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x023C + 4 * ((ch) / 32)) : \
(0x0124 + 4 * ((ch) / 32)); })
#define IPU_ALT_CUR_BUF0(type) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0244) : \
(0x012C); })
#define IPU_ALT_CUR_BUF1(type) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0248) : \
(0x0130); })
#define IPU_SRM_STAT(type) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x024C) : \
(0x0134); })
#define IPU_PROC_TASK_STAT(type) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0250) : \
(0x0138); })
#define IPU_DISP_TASK_STAT(type) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0254) : \
(0x013C); })
#define IPU_CHA_BUF0_RDY(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0268 + 4 * ((ch) / 32)) : \
(0x0140 + 4 * ((ch) / 32)); })
#define IPU_CHA_BUF1_RDY(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0270 + 4 * ((ch) / 32)) : \
(0x0148 + 4 * ((ch) / 32)); })
#define IPU_ALT_CHA_BUF0_RDY(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0278 + 4 * ((ch) / 32)) : \
(0x0158 + 4 * ((ch) / 32)); })
#define IPU_ALT_CHA_BUF1_RDY(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0280 + 4 * ((ch) / 32)) : \
(0x0160 + 4 * ((ch) / 32)); })
#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * ((n) - 1))
#define IPU_INT_STAT(type, n) IPU_CM_REG({(type) >= IPUv3EX ? \
(0x0200 + 4 * ((n) - 1)) : \
(0x00E8 + 4 * ((n) - 1)); })
#define IPUIRQ_2_STATREG(type, irq) IPU_CM_REG(IPU_INT_STAT((type), 1) + 4 \
* ((irq) / 32))
#define IPUIRQ_2_CTRLREG(irq) IPU_CM_REG(IPU_INT_CTRL(1) + 4 * ((irq) / 32))
#define IPUIRQ_2_MASK(irq) (1UL << ((irq) & 0x1F))
/* IPU VDI registers */
#define IPU_VDI_REG(offset) (offset)
#define VDI_FSIZE IPU_VDI_REG(0)
#define VDI_C IPU_VDI_REG(0x0004)
/* IPU CSI Registers */
#define IPU_CSI_REG(offset) (offset)
#define CSI_SENS_CONF IPU_CSI_REG(0)
#define CSI_SENS_FRM_SIZE IPU_CSI_REG(0x0004)
#define CSI_ACT_FRM_SIZE IPU_CSI_REG(0x0008)
#define CSI_OUT_FRM_CTRL IPU_CSI_REG(0x000C)
#define CSI_TST_CTRL IPU_CSI_REG(0x0010)
#define CSI_CCIR_CODE_1 IPU_CSI_REG(0x0014)
#define CSI_CCIR_CODE_2 IPU_CSI_REG(0x0018)
#define CSI_CCIR_CODE_3 IPU_CSI_REG(0x001C)
#define CSI_MIPI_DI IPU_CSI_REG(0x0020)
#define CSI_SKIP IPU_CSI_REG(0x0024)
#define CSI_CPD_CTRL IPU_CSI_REG(0x0028)
#define CSI_CPD_RC(n) IPU_CSI_REG(0x002C + 4 * (n))
#define CSI_CPD_RS(n) IPU_CSI_REG(0x004C + 4 * (n))
#define CSI_CPD_GRC(n) IPU_CSI_REG(0x005C + 4 * (n))
#define CSI_CPD_GRS(n) IPU_CSI_REG(0x007C + 4 * (n))
#define CSI_CPD_GBC(n) IPU_CSI_REG(0x008C + 4 * (n))
#define CSI_CPD_GBS(n) IPU_CSI_REG(0x00AC + 4 * (n))
#define CSI_CPD_BC(n) IPU_CSI_REG(0x00BC + 4 * (n))
#define CSI_CPD_BS(n) IPU_CSI_REG(0x00DC + 4 * (n))
#define CSI_CPD_OFFSET1 IPU_CSI_REG(0x00EC)
#define CSI_CPD_OFFSET2 IPU_CSI_REG(0x00F0)
/* IPU SMFC Registers */
#define IPU_SMFC_REG(offset) (offset)
#define SMFC_MAP IPU_SMFC_REG(0)
#define SMFC_WMC IPU_SMFC_REG(0x0004)
#define SMFC_BS IPU_SMFC_REG(0x0008)
/* IPU IC Registers */
#define IPU_IC_REG(offset) (offset)
#define IC_CONF IPU_IC_REG(0)
#define IC_PRP_ENC_RSC IPU_IC_REG(0x0004)
#define IC_PRP_VF_RSC IPU_IC_REG(0x0008)
#define IC_PP_RSC IPU_IC_REG(0x000C)
#define IC_CMBP_1 IPU_IC_REG(0x0010)
#define IC_CMBP_2 IPU_IC_REG(0x0014)
#define IC_IDMAC_1 IPU_IC_REG(0x0018)
#define IC_IDMAC_2 IPU_IC_REG(0x001C)
#define IC_IDMAC_3 IPU_IC_REG(0x0020)
#define IC_IDMAC_4 IPU_IC_REG(0x0024)
/* IPU IDMAC Registers */
#define IPU_IDMAC_REG(offset) (offset)
#define IDMAC_CONF IPU_IDMAC_REG(0x0000)
#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32))
#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000C)
#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010)
#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32))
#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001C + 4 * ((ch) / 32))
#define IDMAC_CH_LOCK_EN_1(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
(0x0024) : 0; })
#define IDMAC_CH_LOCK_EN_2(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
(0x0028) : \
(0x0024); })
#define IDMAC_SUB_ADDR_0(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
(0x002C) : \
(0x0028); })
#define IDMAC_SUB_ADDR_1(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
(0x0030) : \
(0x002C); })
#define IDMAC_SUB_ADDR_2(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
(0x0034) : \
(0x0030); })
/*
* IPUv3D doesn't support IDMAC_SUB_ADDR_3 and IDMAC_SUB_ADDR_4,
* so point them to readonly IDMAC_CHA_BUSY1 for IPUv3D.
*/
#define IDMAC_SUB_ADDR_3(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
(0x0038) : \
(0x0040); })
#define IDMAC_SUB_ADDR_4(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
(0x003C) : \
(0x0040); })
#define IDMAC_BAND_EN(type, ch) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
(0x0040 + 4 * ((ch) / 32)) : \
(0x0034 + 4 * ((ch) / 32)); })
#define IDMAC_CHA_BUSY(type, ch) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
(0x0100 + 4 * ((ch) / 32)) : \
(0x0040 + 4 * ((ch) / 32)); })
/* IPU DI Registers */
#define IPU_DI_REG(offset) (offset)
#define DI_GENERAL IPU_DI_REG(0)
#define DI_BS_CLKGEN0 IPU_DI_REG(0x0004)
#define DI_BS_CLKGEN1 IPU_DI_REG(0x0008)
#define DI_SW_GEN0(gen) IPU_DI_REG(0x000C + 4 * ((gen) - 1))
#define DI_SW_GEN1(gen) IPU_DI_REG(0x0030 + 4 * ((gen) - 1))
#define DI_STP_REP(gen) IPU_DI_REG(0x0148 + 4 * (((gen) - 1) / 2))
#define DI_SYNC_AS_GEN IPU_DI_REG(0x0054)
#define DI_DW_GEN(gen) IPU_DI_REG(0x0058 + 4 * (gen))
#define DI_DW_SET(gen, set) IPU_DI_REG(0x0088 + 4 * ((gen) + 0xC * (set)))
#define DI_SER_CONF IPU_DI_REG(0x015C)
#define DI_SSC IPU_DI_REG(0x0160)
#define DI_POL IPU_DI_REG(0x0164)
#define DI_AW0 IPU_DI_REG(0x0168)
#define DI_AW1 IPU_DI_REG(0x016C)
#define DI_SCR_CONF IPU_DI_REG(0x0170)
#define DI_STAT IPU_DI_REG(0x0174)
/* IPU DMFC Registers */
#define IPU_DMFC_REG(offset) (offset)
#define DMFC_RD_CHAN IPU_DMFC_REG(0)
#define DMFC_WR_CHAN IPU_DMFC_REG(0x0004)
#define DMFC_WR_CHAN_DEF IPU_DMFC_REG(0x0008)
#define DMFC_DP_CHAN IPU_DMFC_REG(0x000C)
#define DMFC_DP_CHAN_DEF IPU_DMFC_REG(0x0010)
#define DMFC_GENERAL1 IPU_DMFC_REG(0x0014)
#define DMFC_GENERAL2 IPU_DMFC_REG(0x0018)
#define DMFC_IC_CTRL IPU_DMFC_REG(0x001C)
#define DMFC_STAT IPU_DMFC_REG(0x0020)
/* IPU DC Registers */
#define IPU_DC_REG(offset) (offset)
#define DC_MAP_CONF_PTR(n) IPU_DC_REG(0x0108 + ((n) & ~0x1) * 2)
#define DC_MAP_CONF_VAL(n) IPU_DC_REG(0x0144 + ((n) & ~0x1) * 2)
#define _RL_CH_2_OFFSET(ch) (((ch) == 0) ? 8 : ( \
((ch) == 1) ? 0x24 : ( \
((ch) == 2) ? 0x40 : ( \
((ch) == 5) ? 0x64 : ( \
((ch) == 6) ? 0x80 : ( \
((ch) == 8) ? 0x9C : ( \
((ch) == 9) ? 0xBC : (-1))))))))
#define DC_RL_CH(ch, evt) IPU_DC_REG(_RL_CH_2_OFFSET(ch) + \
((evt) & ~0x1) * 2)
#define DC_EVT_NF 0
#define DC_EVT_NL 1
#define DC_EVT_EOF 2
#define DC_EVT_NFIELD 3
#define DC_EVT_EOL 4
#define DC_EVT_EOFIELD 5
#define DC_EVT_NEW_ADDR 6
#define DC_EVT_NEW_CHAN 7
#define DC_EVT_NEW_DATA 8
#define DC_EVT_NEW_ADDR_W_0 0
#define DC_EVT_NEW_ADDR_W_1 1
#define DC_EVT_NEW_CHAN_W_0 2
#define DC_EVT_NEW_CHAN_W_1 3
#define DC_EVT_NEW_DATA_W_0 4
#define DC_EVT_NEW_DATA_W_1 5
#define DC_EVT_NEW_ADDR_R_0 6
#define DC_EVT_NEW_ADDR_R_1 7
#define DC_EVT_NEW_CHAN_R_0 8
#define DC_EVT_NEW_CHAN_R_1 9
#define DC_EVT_NEW_DATA_R_0 10
#define DC_EVT_NEW_DATA_R_1 11
#define DC_EVEN_UGDE0 12
#define DC_ODD_UGDE0 13
#define DC_EVEN_UGDE1 14
#define DC_ODD_UGDE1 15
#define DC_EVEN_UGDE2 16
#define DC_ODD_UGDE2 17
#define DC_EVEN_UGDE3 18
#define DC_ODD_UGDE3 19
#define dc_ch_offset(ch) \
({ \
const u8 _offset[] = { \
0, 0x1C, 0x38, 0x54, 0x58, 0x5C, 0x78, 0, 0x94, 0xB4}; \
_offset[ch]; \
})
#define DC_WR_CH_CONF(ch) IPU_DC_REG(dc_ch_offset(ch))
#define DC_WR_CH_ADDR(ch) IPU_DC_REG(dc_ch_offset(ch) + 4)
#define DC_WR_CH_CONF_1 IPU_DC_REG(0x001C)
#define DC_WR_CH_ADDR_1 IPU_DC_REG(0x0020)
#define DC_WR_CH_CONF_5 IPU_DC_REG(0x005C)
#define DC_WR_CH_ADDR_5 IPU_DC_REG(0x0060)
#define DC_GEN IPU_DC_REG(0x00D4)
#define DC_DISP_CONF1(disp) IPU_DC_REG(0x00D8 + 4 * (disp))
#define DC_DISP_CONF2(disp) IPU_DC_REG(0x00E8 + 4 * (disp))
#define DC_STAT IPU_DC_REG(0x01C8)
#define DC_UGDE_0(evt) IPU_DC_REG(0x0174 + 16 * (evt))
#define DC_UGDE_1(evt) IPU_DC_REG(0x0178 + 16 * (evt))
#define DC_UGDE_2(evt) IPU_DC_REG(0x017C + 16 * (evt))
#define DC_UGDE_3(evt) IPU_DC_REG(0x0180 + 16 * (evt))
/* IPU DP Registers */
#define IPU_DP_REG(offset) (offset)
#define DP_SYNC 0
#define DP_ASYNC0 0x60
#define DP_ASYNC1 0xBC
#define DP_COM_CONF(flow) IPU_DP_REG(flow)
#define DP_GRAPH_WIND_CTRL(flow) IPU_DP_REG(0x0004 + (flow))
#define DP_FG_POS(flow) IPU_DP_REG(0x0008 + (flow))
#define DP_GAMMA_C(flow, i) IPU_DP_REG(0x0014 + (flow) + 4 * (i))
#define DP_GAMMA_S(flow, i) IPU_DP_REG(0x0034 + (flow) + 4 * (i))
#define DP_CSC_A_0(flow) IPU_DP_REG(0x0044 + (flow))
#define DP_CSC_A_1(flow) IPU_DP_REG(0x0048 + (flow))
#define DP_CSC_A_2(flow) IPU_DP_REG(0x004C + (flow))
#define DP_CSC_A_3(flow) IPU_DP_REG(0x0050 + (flow))
#define DP_CSC_0(flow) IPU_DP_REG(0x0054 + (flow))
#define DP_CSC_1(flow) IPU_DP_REG(0x0058 + (flow))
enum {
IPU_CONF_CSI0_EN = 0x00000001,
IPU_CONF_CSI1_EN = 0x00000002,
IPU_CONF_IC_EN = 0x00000004,
IPU_CONF_ROT_EN = 0x00000008,
IPU_CONF_ISP_EN = 0x00000010,
IPU_CONF_DP_EN = 0x00000020,
IPU_CONF_DI0_EN = 0x00000040,
IPU_CONF_DI1_EN = 0x00000080,
IPU_CONF_DMFC_EN = 0x00000400,
IPU_CONF_SMFC_EN = 0x00000100,
IPU_CONF_DC_EN = 0x00000200,
IPU_CONF_VDI_EN = 0x00001000,
IPU_CONF_IDMAC_DIS = 0x00400000,
IPU_CONF_IC_DMFC_SEL = 0x02000000,
IPU_CONF_IC_DMFC_SYNC = 0x04000000,
IPU_CONF_VDI_DMFC_SYNC = 0x08000000,
IPU_CONF_CSI0_DATA_SOURCE = 0x10000000,
IPU_CONF_CSI0_DATA_SOURCE_OFFSET = 28,
IPU_CONF_CSI1_DATA_SOURCE = 0x20000000,
IPU_CONF_IC_INPUT = 0x40000000,
IPU_CONF_CSI_SEL = 0x80000000,
DI0_COUNTER_RELEASE = 0x01000000,
DI1_COUNTER_RELEASE = 0x02000000,
FS_PRPVF_ROT_SRC_SEL_MASK = 0x00000F00,
FS_PRPVF_ROT_SRC_SEL_OFFSET = 8,
FS_PRPENC_ROT_SRC_SEL_MASK = 0x0000000F,
FS_PRPENC_ROT_SRC_SEL_OFFSET = 0,
FS_PP_ROT_SRC_SEL_MASK = 0x000F0000,
FS_PP_ROT_SRC_SEL_OFFSET = 16,
FS_PP_SRC_SEL_MASK = 0x0000F000,
FS_PP_SRC_SEL_VDOA = 0x00008000,
FS_PP_SRC_SEL_OFFSET = 12,
FS_PRP_SRC_SEL_MASK = 0x0F000000,
FS_PRP_SRC_SEL_OFFSET = 24,
FS_VF_IN_VALID = 0x80000000,
FS_ENC_IN_VALID = 0x40000000,
FS_VDI_SRC_SEL_MASK = 0x30000000,
FS_VDI_SRC_SEL_VDOA = 0x20000000,
FS_VDOA_DEST_SEL_MASK = 0x00030000,
FS_VDOA_DEST_SEL_VDI = 0x00020000,
FS_VDOA_DEST_SEL_IC = 0x00010000,
FS_VDI_SRC_SEL_OFFSET = 28,
FS_PRPENC_DEST_SEL_MASK = 0x0000000F,
FS_PRPENC_DEST_SEL_OFFSET = 0,
FS_PRPVF_DEST_SEL_MASK = 0x000000F0,
FS_PRPVF_DEST_SEL_OFFSET = 4,
FS_PRPVF_ROT_DEST_SEL_MASK = 0x00000F00,
FS_PRPVF_ROT_DEST_SEL_OFFSET = 8,
FS_PP_DEST_SEL_MASK = 0x0000F000,
FS_PP_DEST_SEL_OFFSET = 12,
FS_PP_ROT_DEST_SEL_MASK = 0x000F0000,
FS_PP_ROT_DEST_SEL_OFFSET = 16,
FS_PRPENC_ROT_DEST_SEL_MASK = 0x00F00000,
FS_PRPENC_ROT_DEST_SEL_OFFSET = 20,
FS_SMFC0_DEST_SEL_MASK = 0x0000000F,
FS_SMFC0_DEST_SEL_OFFSET = 0,
FS_SMFC1_DEST_SEL_MASK = 0x00000070,
FS_SMFC1_DEST_SEL_OFFSET = 4,
FS_SMFC2_DEST_SEL_MASK = 0x00000780,
FS_SMFC2_DEST_SEL_OFFSET = 7,
FS_SMFC3_DEST_SEL_MASK = 0x00003800,
FS_SMFC3_DEST_SEL_OFFSET = 11,
FS_DC1_SRC_SEL_MASK = 0x00F00000,
FS_DC1_SRC_SEL_OFFSET = 20,
FS_DC2_SRC_SEL_MASK = 0x000F0000,
FS_DC2_SRC_SEL_OFFSET = 16,
FS_DP_SYNC0_SRC_SEL_MASK = 0x0000000F,
FS_DP_SYNC0_SRC_SEL_OFFSET = 0,
FS_DP_SYNC1_SRC_SEL_MASK = 0x000000F0,
FS_DP_SYNC1_SRC_SEL_OFFSET = 4,
FS_DP_ASYNC0_SRC_SEL_MASK = 0x00000F00,
FS_DP_ASYNC0_SRC_SEL_OFFSET = 8,
FS_DP_ASYNC1_SRC_SEL_MASK = 0x0000F000,
FS_DP_ASYNC1_SRC_SEL_OFFSET = 12,
FS_AUTO_REF_PER_MASK = 0,
FS_AUTO_REF_PER_OFFSET = 16,
TSTAT_VF_MASK = 0x0000000C,
TSTAT_VF_OFFSET = 2,
TSTAT_VF_ROT_MASK = 0x00000300,
TSTAT_VF_ROT_OFFSET = 8,
TSTAT_ENC_MASK = 0x00000003,
TSTAT_ENC_OFFSET = 0,
TSTAT_ENC_ROT_MASK = 0x000000C0,
TSTAT_ENC_ROT_OFFSET = 6,
TSTAT_PP_MASK = 0x00000030,
TSTAT_PP_OFFSET = 4,
TSTAT_PP_ROT_MASK = 0x00000C00,
TSTAT_PP_ROT_OFFSET = 10,
TASK_STAT_IDLE = 0,
TASK_STAT_ACTIVE = 1,
TASK_STAT_WAIT4READY = 2,
/* IDMAC register bits */
IDMAC_CONF_USED_BUFS_EN_R = 0x02000000,
IDMAC_CONF_USED_BUFS_MAX_R_MASK = 0x01E00000,
IDMAC_CONF_USED_BUFS_MAX_R_OFFSET = 21,
IDMAC_CONF_USED_BUFS_EN_W = 0x00100000,
IDMAC_CONF_USED_BUFS_MAX_W_MASK = 0x000E0000,
IDMAC_CONF_USED_BUFS_MAX_W_OFFSET = 17,
/* Image Converter Register bits */
IC_CONF_PRPENC_EN = 0x00000001,
IC_CONF_PRPENC_CSC1 = 0x00000002,
IC_CONF_PRPENC_ROT_EN = 0x00000004,
IC_CONF_PRPVF_EN = 0x00000100,
IC_CONF_PRPVF_CSC1 = 0x00000200,
IC_CONF_PRPVF_CSC2 = 0x00000400,
IC_CONF_PRPVF_CMB = 0x00000800,
IC_CONF_PRPVF_ROT_EN = 0x00001000,
IC_CONF_PP_EN = 0x00010000,
IC_CONF_PP_CSC1 = 0x00020000,
IC_CONF_PP_CSC2 = 0x00040000,
IC_CONF_PP_CMB = 0x00080000,
IC_CONF_PP_ROT_EN = 0x00100000,
IC_CONF_IC_GLB_LOC_A = 0x10000000,
IC_CONF_KEY_COLOR_EN = 0x20000000,
IC_CONF_RWS_EN = 0x40000000,
IC_CONF_CSI_MEM_WR_EN = 0x80000000,
IC_RSZ_MAX_RESIZE_RATIO = 0x00004000,
IC_IDMAC_1_CB0_BURST_16 = 0x00000001,
IC_IDMAC_1_CB1_BURST_16 = 0x00000002,
IC_IDMAC_1_CB2_BURST_16 = 0x00000004,
IC_IDMAC_1_CB3_BURST_16 = 0x00000008,
IC_IDMAC_1_CB4_BURST_16 = 0x00000010,
IC_IDMAC_1_CB5_BURST_16 = 0x00000020,
IC_IDMAC_1_CB6_BURST_16 = 0x00000040,
IC_IDMAC_1_CB7_BURST_16 = 0x00000080,
IC_IDMAC_1_PRPENC_ROT_MASK = 0x00003800,
IC_IDMAC_1_PRPENC_ROT_OFFSET = 11,
IC_IDMAC_1_PRPVF_ROT_MASK = 0x0001C000,
IC_IDMAC_1_PRPVF_ROT_OFFSET = 14,
IC_IDMAC_1_PP_ROT_MASK = 0x000E0000,
IC_IDMAC_1_PP_ROT_OFFSET = 17,
IC_IDMAC_1_PP_FLIP_RS = 0x00400000,
IC_IDMAC_1_PRPVF_FLIP_RS = 0x00200000,
IC_IDMAC_1_PRPENC_FLIP_RS = 0x00100000,
IC_IDMAC_2_PRPENC_HEIGHT_MASK = 0x000003FF,
IC_IDMAC_2_PRPENC_HEIGHT_OFFSET = 0,
IC_IDMAC_2_PRPVF_HEIGHT_MASK = 0x000FFC00,
IC_IDMAC_2_PRPVF_HEIGHT_OFFSET = 10,
IC_IDMAC_2_PP_HEIGHT_MASK = 0x3FF00000,
IC_IDMAC_2_PP_HEIGHT_OFFSET = 20,
IC_IDMAC_3_PRPENC_WIDTH_MASK = 0x000003FF,
IC_IDMAC_3_PRPENC_WIDTH_OFFSET = 0,
IC_IDMAC_3_PRPVF_WIDTH_MASK = 0x000FFC00,
IC_IDMAC_3_PRPVF_WIDTH_OFFSET = 10,
IC_IDMAC_3_PP_WIDTH_MASK = 0x3FF00000,
IC_IDMAC_3_PP_WIDTH_OFFSET = 20,
CSI_SENS_CONF_DATA_FMT_SHIFT = 8,
CSI_SENS_CONF_DATA_FMT_MASK = 0x00000700,
CSI_SENS_CONF_DATA_FMT_RGB_YUV444 = 0L,
CSI_SENS_CONF_DATA_FMT_YUV422_YUYV = 1L,
CSI_SENS_CONF_DATA_FMT_YUV422_UYVY = 2L,
CSI_SENS_CONF_DATA_FMT_BAYER = 3L,
CSI_SENS_CONF_DATA_FMT_RGB565 = 4L,
CSI_SENS_CONF_DATA_FMT_RGB555 = 5L,
CSI_SENS_CONF_DATA_FMT_RGB444 = 6L,
CSI_SENS_CONF_DATA_FMT_JPEG = 7L,
CSI_SENS_CONF_VSYNC_POL_SHIFT = 0,
CSI_SENS_CONF_HSYNC_POL_SHIFT = 1,
CSI_SENS_CONF_DATA_POL_SHIFT = 2,
CSI_SENS_CONF_PIX_CLK_POL_SHIFT = 3,
CSI_SENS_CONF_SENS_PRTCL_MASK = 0x00000070L,
CSI_SENS_CONF_SENS_PRTCL_SHIFT = 4,
CSI_SENS_CONF_PACK_TIGHT_SHIFT = 7,
CSI_SENS_CONF_DATA_WIDTH_SHIFT = 11,
CSI_SENS_CONF_EXT_VSYNC_SHIFT = 15,
CSI_SENS_CONF_DIVRATIO_SHIFT = 16,
CSI_SENS_CONF_DIVRATIO_MASK = 0x00FF0000L,
CSI_SENS_CONF_DATA_DEST_SHIFT = 24,
CSI_SENS_CONF_DATA_DEST_MASK = 0x07000000L,
CSI_SENS_CONF_JPEG8_EN_SHIFT = 27,
CSI_SENS_CONF_JPEG_EN_SHIFT = 28,
CSI_SENS_CONF_FORCE_EOF_SHIFT = 29,
CSI_SENS_CONF_DATA_EN_POL_SHIFT = 31,
CSI_DATA_DEST_ISP = 1L,
CSI_DATA_DEST_IC = 2L,
CSI_DATA_DEST_IDMAC = 4L,
CSI_CCIR_ERR_DET_EN = 0x01000000L,
CSI_HORI_DOWNSIZE_EN = 0x80000000L,
CSI_VERT_DOWNSIZE_EN = 0x40000000L,
CSI_TEST_GEN_MODE_EN = 0x01000000L,
CSI_HSC_MASK = 0x1FFF0000,
CSI_HSC_SHIFT = 16,
CSI_VSC_MASK = 0x00000FFF,
CSI_VSC_SHIFT = 0,
CSI_TEST_GEN_R_MASK = 0x000000FFL,
CSI_TEST_GEN_R_SHIFT = 0,
CSI_TEST_GEN_G_MASK = 0x0000FF00L,
CSI_TEST_GEN_G_SHIFT = 8,
CSI_TEST_GEN_B_MASK = 0x00FF0000L,
CSI_TEST_GEN_B_SHIFT = 16,
CSI_MIPI_DI0_MASK = 0x000000FFL,
CSI_MIPI_DI0_SHIFT = 0,
CSI_MIPI_DI1_MASK = 0x0000FF00L,
CSI_MIPI_DI1_SHIFT = 8,
CSI_MIPI_DI2_MASK = 0x00FF0000L,
CSI_MIPI_DI2_SHIFT = 16,
CSI_MIPI_DI3_MASK = 0xFF000000L,
CSI_MIPI_DI3_SHIFT = 24,
CSI_MAX_RATIO_SKIP_ISP_MASK = 0x00070000L,
CSI_MAX_RATIO_SKIP_ISP_SHIFT = 16,
CSI_SKIP_ISP_MASK = 0x00F80000L,
CSI_SKIP_ISP_SHIFT = 19,
CSI_MAX_RATIO_SKIP_SMFC_MASK = 0x00000007L,
CSI_MAX_RATIO_SKIP_SMFC_SHIFT = 0,
CSI_SKIP_SMFC_MASK = 0x000000F8L,
CSI_SKIP_SMFC_SHIFT = 3,
CSI_ID_2_SKIP_MASK = 0x00000300L,
CSI_ID_2_SKIP_SHIFT = 8,
CSI_COLOR_FIRST_ROW_MASK = 0x00000002L,
CSI_COLOR_FIRST_COMP_MASK = 0x00000001L,
SMFC_MAP_CH0_MASK = 0x00000007L,
SMFC_MAP_CH0_SHIFT = 0,
SMFC_MAP_CH1_MASK = 0x00000038L,
SMFC_MAP_CH1_SHIFT = 3,
SMFC_MAP_CH2_MASK = 0x000001C0L,
SMFC_MAP_CH2_SHIFT = 6,
SMFC_MAP_CH3_MASK = 0x00000E00L,
SMFC_MAP_CH3_SHIFT = 9,
SMFC_WM0_SET_MASK = 0x00000007L,
SMFC_WM0_SET_SHIFT = 0,
SMFC_WM1_SET_MASK = 0x000001C0L,
SMFC_WM1_SET_SHIFT = 6,
SMFC_WM2_SET_MASK = 0x00070000L,
SMFC_WM2_SET_SHIFT = 16,
SMFC_WM3_SET_MASK = 0x01C00000L,
SMFC_WM3_SET_SHIFT = 22,
SMFC_WM0_CLR_MASK = 0x00000038L,
SMFC_WM0_CLR_SHIFT = 3,
SMFC_WM1_CLR_MASK = 0x00000E00L,
SMFC_WM1_CLR_SHIFT = 9,
SMFC_WM2_CLR_MASK = 0x00380000L,
SMFC_WM2_CLR_SHIFT = 19,
SMFC_WM3_CLR_MASK = 0x0E000000L,
SMFC_WM3_CLR_SHIFT = 25,
SMFC_BS0_MASK = 0x0000000FL,
SMFC_BS0_SHIFT = 0,
SMFC_BS1_MASK = 0x000000F0L,
SMFC_BS1_SHIFT = 4,
SMFC_BS2_MASK = 0x00000F00L,
SMFC_BS2_SHIFT = 8,
SMFC_BS3_MASK = 0x0000F000L,
SMFC_BS3_SHIFT = 12,
PF_CONF_TYPE_MASK = 0x00000007,
PF_CONF_TYPE_SHIFT = 0,
PF_CONF_PAUSE_EN = 0x00000010,
PF_CONF_RESET = 0x00008000,
PF_CONF_PAUSE_ROW_MASK = 0x00FF0000,
PF_CONF_PAUSE_ROW_SHIFT = 16,
DI_DW_GEN_ACCESS_SIZE_OFFSET = 24,
DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16,
DI_GEN_DI_CLK_EXT = 0x100000,
DI_GEN_POLARITY_DISP_CLK = 0x00020000,
DI_GEN_POLARITY_1 = 0x00000001,
DI_GEN_POLARITY_2 = 0x00000002,
DI_GEN_POLARITY_3 = 0x00000004,
DI_GEN_POLARITY_4 = 0x00000008,
DI_GEN_POLARITY_5 = 0x00000010,
DI_GEN_POLARITY_6 = 0x00000020,
DI_GEN_POLARITY_7 = 0x00000040,
DI_GEN_POLARITY_8 = 0x00000080,
DI_POL_DRDY_DATA_POLARITY = 0x00000080,
DI_POL_DRDY_POLARITY_15 = 0x00000010,
DI_VSYNC_SEL_OFFSET = 13,
DC_WR_CH_CONF_FIELD_MODE = 0x00000200,
DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5,
DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0,
DC_WR_CH_CONF_PROG_DI_ID = 0x00000004,
DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3,
DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018,
DC_UGDE_0_ODD_EN = 0x02000000,
DC_UGDE_0_ID_CODED_MASK = 0x00000007,
DC_UGDE_0_ID_CODED_OFFSET = 0,
DC_UGDE_0_EV_PRIORITY_MASK = 0x00000078,
DC_UGDE_0_EV_PRIORITY_OFFSET = 3,
DP_COM_CONF_FG_EN = 0x00000001,
DP_COM_CONF_GWSEL = 0x00000002,
DP_COM_CONF_GWAM = 0x00000004,
DP_COM_CONF_GWCKE = 0x00000008,
DP_COM_CONF_CSC_DEF_MASK = 0x00000300,
DP_COM_CONF_CSC_DEF_OFFSET = 8,
DP_COM_CONF_CSC_DEF_FG = 0x00000300,
DP_COM_CONF_CSC_DEF_BG = 0x00000200,
DP_COM_CONF_CSC_DEF_BOTH = 0x00000100,
DP_COM_CONF_GAMMA_EN = 0x00001000,
DP_COM_CONF_GAMMA_YUV_EN = 0x00002000,
DI_SER_CONF_LLA_SER_ACCESS = 0x00000020,
DI_SER_CONF_SERIAL_CLK_POL = 0x00000010,
DI_SER_CONF_SERIAL_DATA_POL = 0x00000008,
DI_SER_CONF_SERIAL_RS_POL = 0x00000004,
DI_SER_CONF_SERIAL_CS_POL = 0x00000002,
DI_SER_CONF_WAIT4SERIAL = 0x00000001,
VDI_C_CH_420 = 0x00000000,
VDI_C_CH_422 = 0x00000002,
VDI_C_MOT_SEL_FULL = 0x00000008,
VDI_C_MOT_SEL_LOW = 0x00000004,
VDI_C_MOT_SEL_MED = 0x00000000,
VDI_C_BURST_SIZE1_4 = 0x00000030,
VDI_C_BURST_SIZE2_4 = 0x00000300,
VDI_C_BURST_SIZE3_4 = 0x00003000,
VDI_C_BURST_SIZE_MASK = 0xF,
VDI_C_BURST_SIZE1_OFFSET = 4,
VDI_C_BURST_SIZE2_OFFSET = 8,
VDI_C_BURST_SIZE3_OFFSET = 12,
VDI_C_VWM1_SET_1 = 0x00000000,
VDI_C_VWM1_SET_2 = 0x00010000,
VDI_C_VWM1_CLR_2 = 0x00080000,
VDI_C_VWM3_SET_1 = 0x00000000,
VDI_C_VWM3_SET_2 = 0x00400000,
VDI_C_VWM3_CLR_2 = 0x02000000,
VDI_C_TOP_FIELD_MAN_1 = 0x40000000,
VDI_C_TOP_FIELD_AUTO_1 = 0x80000000,
};
enum di_pins {
DI_PIN11 = 0,
DI_PIN12 = 1,
DI_PIN13 = 2,
DI_PIN14 = 3,
DI_PIN15 = 4,
DI_PIN16 = 5,
DI_PIN17 = 6,
DI_PIN_CS = 7,
DI_PIN_SER_CLK = 0,
DI_PIN_SER_RS = 1,
};
enum di_sync_wave {
DI_SYNC_NONE = -1,
DI_SYNC_CLK = 0,
DI_SYNC_INT_HSYNC = 1,
DI_SYNC_HSYNC = 2,
DI_SYNC_VSYNC = 3,
DI_SYNC_DE = 5,
};
/* DC template opcodes */
#define WROD(lf) (0x18 | (lf << 1))
#define WRG (0x01)
#endif

View File

@ -0,0 +1,480 @@
/*
* Freescale PRE Register Definitions
*
* Copyright 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __ARCH_ARM___PRE_H
#define __ARCH_ARM___PRE_H
#define HW_PRE_CTRL (0x00000000)
#define HW_PRE_CTRL_SET (0x00000004)
#define HW_PRE_CTRL_CLR (0x00000008)
#define HW_PRE_CTRL_TOG (0x0000000c)
#define BM_PRE_CTRL_SFTRST 0x80000000
#define BF_PRE_CTRL_SFTRST(v) \
(((v) << 31) & BM_PRE_CTRL_SFTRST)
#define BM_PRE_CTRL_CLKGATE 0x40000000
#define BF_PRE_CTRL_CLKGATE(v) \
(((v) << 30) & BM_PRE_CTRL_CLKGATE)
#define BM_PRE_CTRL_TPR_RESET_SEL 0x20000000
#define BF_PRE_CTRL_TPR_RESET_SEL(v) \
(((v) << 29) & BM_PRE_CTRL_TPR_RESET_SEL)
#define BM_PRE_CTRL_EN_REPEAT 0x10000000
#define BF_PRE_CTRL_EN_REPEAT(v) \
(((v) << 28) & BM_PRE_CTRL_EN_REPEAT)
#define BP_PRE_CTRL_RSVD2 16
#define BM_PRE_CTRL_RSVD2 0x0FFF0000
#define BF_PRE_CTRL_RSVD2(v) \
(((v) << 16) & BM_PRE_CTRL_RSVD2)
#define BP_PRE_CTRL_RSVD1 12
#define BM_PRE_CTRL_RSVD1 0x0000F000
#define BF_PRE_CTRL_RSVD1(v) \
(((v) << 12) & BM_PRE_CTRL_RSVD1)
#define BM_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN 0x00000800
#define BF_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN(v) \
(((v) << 11) & BM_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN)
#define BV_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN__0 0x0
#define BV_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN__1 0x1
#define BP_PRE_CTRL_HANDSHAKE_LINE_NUM 9
#define BM_PRE_CTRL_HANDSHAKE_LINE_NUM 0x00000600
#define BF_PRE_CTRL_HANDSHAKE_LINE_NUM(v) \
(((v) << 9) & BM_PRE_CTRL_HANDSHAKE_LINE_NUM)
#define BV_PRE_CTRL_HANDSHAKE_LINE_NUM__0 0x0
#define BV_PRE_CTRL_HANDSHAKE_LINE_NUM__1 0x1
#define BV_PRE_CTRL_HANDSHAKE_LINE_NUM__2 0x2
#define BM_PRE_CTRL_HANDSHAKE_EN 0x00000100
#define BF_PRE_CTRL_HANDSHAKE_EN(v) \
(((v) << 8) & BM_PRE_CTRL_HANDSHAKE_EN)
#define BV_PRE_CTRL_HANDSHAKE_EN__0 0x0
#define BV_PRE_CTRL_HANDSHAKE_EN__1 0x1
#define BM_PRE_CTRL_INTERLACED_FIELD 0x00000080
#define BF_PRE_CTRL_INTERLACED_FIELD(v) \
(((v) << 7) & BM_PRE_CTRL_INTERLACED_FIELD)
#define BM_PRE_CTRL_SO 0x00000040
#define BF_PRE_CTRL_SO(v) \
(((v) << 6) & BM_PRE_CTRL_SO)
#define BM_PRE_CTRL_VFLIP 0x00000020
#define BF_PRE_CTRL_VFLIP(v) \
(((v) << 5) & BM_PRE_CTRL_VFLIP)
#define BM_PRE_CTRL_SDW_UPDATE 0x00000010
#define BF_PRE_CTRL_SDW_UPDATE(v) \
(((v) << 4) & BM_PRE_CTRL_SDW_UPDATE)
#define BM_PRE_CTRL_RSVD0 0x00000008
#define BF_PRE_CTRL_RSVD0(v) \
(((v) << 3) & BM_PRE_CTRL_RSVD0)
#define BM_PRE_CTRL_BLOCK_16 0x00000004
#define BF_PRE_CTRL_BLOCK_16(v) \
(((v) << 2) & BM_PRE_CTRL_BLOCK_16)
#define BV_PRE_CTRL_BLOCK_16__32x4 0x0
#define BV_PRE_CTRL_BLOCK_16__16x4 0x1
#define BM_PRE_CTRL_BLOCK_EN 0x00000002
#define BF_PRE_CTRL_BLOCK_EN(v) \
(((v) << 1) & BM_PRE_CTRL_BLOCK_EN)
#define BV_PRE_CTRL_BLOCK_EN__0 0x0
#define BV_PRE_CTRL_BLOCK_EN__1 0x1
#define BM_PRE_CTRL_ENABLE 0x00000001
#define BF_PRE_CTRL_ENABLE(v) \
(((v) << 0) & BM_PRE_CTRL_ENABLE)
#define HW_PRE_IRQ_MASK (0x00000010)
#define HW_PRE_IRQ_MASK_SET (0x00000014)
#define HW_PRE_IRQ_MASK_CLR (0x00000018)
#define HW_PRE_IRQ_MASK_TOG (0x0000001c)
#define BP_PRE_IRQ_MASK_RSVD1 4
#define BM_PRE_IRQ_MASK_RSVD1 0xFFFFFFF0
#define BF_PRE_IRQ_MASK_RSVD1(v) \
(((v) << 4) & BM_PRE_IRQ_MASK_RSVD1)
#define BM_PRE_IRQ_MASK_TPR_RD_NUM_BYTES_OVFL_IRQ_EN 0x00000008
#define BF_PRE_IRQ_MASK_TPR_RD_NUM_BYTES_OVFL_IRQ_EN(v) \
(((v) << 3) & BM_PRE_IRQ_MASK_TPR_RD_NUM_BYTES_OVFL_IRQ_EN)
#define BM_PRE_IRQ_MASK_HANDSHAKE_ABORT_IRQ_EN 0x00000004
#define BF_PRE_IRQ_MASK_HANDSHAKE_ABORT_IRQ_EN(v) \
(((v) << 2) & BM_PRE_IRQ_MASK_HANDSHAKE_ABORT_IRQ_EN)
#define BM_PRE_IRQ_MASK_STORE_IRQ_EN 0x00000002
#define BF_PRE_IRQ_MASK_STORE_IRQ_EN(v) \
(((v) << 1) & BM_PRE_IRQ_MASK_STORE_IRQ_EN)
#define BM_PRE_IRQ_MASK_PREFETCH_IRQ_EN 0x00000001
#define BF_PRE_IRQ_MASK_PREFETCH_IRQ_EN(v) \
(((v) << 0) & BM_PRE_IRQ_MASK_PREFETCH_IRQ_EN)
#define HW_PRE_IRQ (0x00000020)
#define HW_PRE_IRQ_SET (0x00000024)
#define HW_PRE_IRQ_CLR (0x00000028)
#define HW_PRE_IRQ_TOG (0x0000002c)
#define BP_PRE_IRQ_RSVD1 14
#define BM_PRE_IRQ_RSVD1 0xFFFFC000
#define BF_PRE_IRQ_RSVD1(v) \
(((v) << 14) & BM_PRE_IRQ_RSVD1)
#define BP_PRE_IRQ_AXI_ERROR_ID 10
#define BM_PRE_IRQ_AXI_ERROR_ID 0x00003C00
#define BF_PRE_IRQ_AXI_ERROR_ID(v) \
(((v) << 10) & BM_PRE_IRQ_AXI_ERROR_ID)
#define BM_PRE_IRQ_AXI_READ_ERROR 0x00000200
#define BF_PRE_IRQ_AXI_READ_ERROR(v) \
(((v) << 9) & BM_PRE_IRQ_AXI_READ_ERROR)
#define BM_PRE_IRQ_AXI_WRITE_ERROR 0x00000100
#define BF_PRE_IRQ_AXI_WRITE_ERROR(v) \
(((v) << 8) & BM_PRE_IRQ_AXI_WRITE_ERROR)
#define BP_PRE_IRQ_RSVD0 5
#define BM_PRE_IRQ_RSVD0 0x000000E0
#define BF_PRE_IRQ_RSVD0(v) \
(((v) << 5) & BM_PRE_IRQ_RSVD0)
#define BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ 0x00000010
#define BF_PRE_IRQ_HANDSHAKE_ERROR_IRQ(v) \
(((v) << 4) & BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ)
#define BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ 0x00000008
#define BF_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ(v) \
(((v) << 3) & BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ)
#define BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ 0x00000004
#define BF_PRE_IRQ_HANDSHAKE_ABORT_IRQ(v) \
(((v) << 2) & BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ)
#define BM_PRE_IRQ_STORE_IRQ 0x00000002
#define BF_PRE_IRQ_STORE_IRQ(v) \
(((v) << 1) & BM_PRE_IRQ_STORE_IRQ)
#define BM_PRE_IRQ_PREFETCH_IRQ 0x00000001
#define BF_PRE_IRQ_PREFETCH_IRQ(v) \
(((v) << 0) & BM_PRE_IRQ_PREFETCH_IRQ)
#define HW_PRE_CUR_BUF (0x00000030)
#define BP_PRE_CUR_BUF_ADDR 0
#define BM_PRE_CUR_BUF_ADDR 0xFFFFFFFF
#define BF_PRE_CUR_BUF_ADDR(v) (v)
#define HW_PRE_NEXT_BUF (0x00000040)
#define BP_PRE_NEXT_BUF_ADDR 0
#define BM_PRE_NEXT_BUF_ADDR 0xFFFFFFFF
#define BF_PRE_NEXT_BUF_ADDR(v) (v)
#define HW_PRE_U_BUF_OFFSET (0x00000050)
#define BP_PRE_U_BUF_OFFSET_RSVD0 25
#define BM_PRE_U_BUF_OFFSET_RSVD0 0xFE000000
#define BF_PRE_U_BUF_OFFSET_RSVD0(v) \
(((v) << 25) & BM_PRE_U_BUF_OFFSET_RSVD0)
#define BP_PRE_U_BUF_OFFSET_UBO 0
#define BM_PRE_U_BUF_OFFSET_UBO 0x01FFFFFF
#define BF_PRE_U_BUF_OFFSET_UBO(v) \
(((v) << 0) & BM_PRE_U_BUF_OFFSET_UBO)
#define HW_PRE_V_BUF_OFFSET (0x00000060)
#define BP_PRE_V_BUF_OFFSET_RSVD0 25
#define BM_PRE_V_BUF_OFFSET_RSVD0 0xFE000000
#define BF_PRE_V_BUF_OFFSET_RSVD0(v) \
(((v) << 25) & BM_PRE_V_BUF_OFFSET_RSVD0)
#define BP_PRE_V_BUF_OFFSET_VBO 0
#define BM_PRE_V_BUF_OFFSET_VBO 0x01FFFFFF
#define BF_PRE_V_BUF_OFFSET_VBO(v) \
(((v) << 0) & BM_PRE_V_BUF_OFFSET_VBO)
#define HW_PRE_TPR_CTRL (0x00000070)
#define HW_PRE_TPR_CTRL_SET (0x00000074)
#define HW_PRE_TPR_CTRL_CLR (0x00000078)
#define HW_PRE_TPR_CTRL_TOG (0x0000007c)
#define BP_PRE_TPR_CTRL_RSVD 8
#define BM_PRE_TPR_CTRL_RSVD 0xFFFFFF00
#define BF_PRE_TPR_CTRL_RSVD(v) \
(((v) << 8) & BM_PRE_TPR_CTRL_RSVD)
#define BP_PRE_TPR_CTRL_TILE_FORMAT 0
#define BM_PRE_TPR_CTRL_TILE_FORMAT 0x000000FF
#define BF_PRE_TPR_CTRL_TILE_FORMAT(v) \
(((v) << 0) & BM_PRE_TPR_CTRL_TILE_FORMAT)
#define BV_PRE_TPR_CTRL_TILE_FORMAT__BYPASS 0x00
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_SB_ST 0x10
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_SB_SRT 0x50
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_ST 0x20
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_SRT 0x60
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_MST 0xA0
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_MSRT 0xE0
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_SB_ST 0x11
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_SB_SRT 0x51
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_ST 0x21
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_SRT 0x61
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_MST 0xA1
#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_MSRT 0xE1
#define BV_PRE_TPR_CTRL_TILE_FORMAT__VPU8_PRO 0x22
#define BV_PRE_TPR_CTRL_TILE_FORMAT__VPU8_SB_INT 0x13
#define HW_PRE_PREFETCH_ENGINE_CTRL (0x00000080)
#define HW_PRE_PREFETCH_ENGINE_CTRL_SET (0x00000084)
#define HW_PRE_PREFETCH_ENGINE_CTRL_CLR (0x00000088)
#define HW_PRE_PREFETCH_ENGINE_CTRL_TOG (0x0000008c)
#define BP_PRE_PREFETCH_ENGINE_CTRL_RSVD1 16
#define BM_PRE_PREFETCH_ENGINE_CTRL_RSVD1 0xFFFF0000
#define BF_PRE_PREFETCH_ENGINE_CTRL_RSVD1(v) \
(((v) << 16) & BM_PRE_PREFETCH_ENGINE_CTRL_RSVD1)
#define BM_PRE_PREFETCH_ENGINE_CTRL_TPR_COOR_OFFSET_EN 0x00008000
#define BF_PRE_PREFETCH_ENGINE_CTRL_TPR_COOR_OFFSET_EN(v) \
(((v) << 15) & BM_PRE_PREFETCH_ENGINE_CTRL_TPR_COOR_OFFSET_EN)
#define BM_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP 0x00004000
#define BF_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP(v) \
(((v) << 14) & BM_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP)
#define BM_PRE_PREFETCH_ENGINE_CTRL_CROP_EN 0x00002000
#define BF_PRE_PREFETCH_ENGINE_CTRL_CROP_EN(v) \
(((v) << 13) & BM_PRE_PREFETCH_ENGINE_CTRL_CROP_EN)
#define BV_PRE_PREFETCH_ENGINE_CTRL_CROP_EN__0 0x0
#define BV_PRE_PREFETCH_ENGINE_CTRL_CROP_EN__1 0x1
#define BM_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE 0x00001000
#define BF_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE(v) \
(((v) << 12) & BM_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE)
#define BV_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE__0 0x0
#define BV_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE__1 0x1
#define BM_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS 0x00000800
#define BF_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS(v) \
(((v) << 11) & BM_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS)
#define BV_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS__0 0x0
#define BV_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS__1 0x1
#define BP_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT 8
#define BM_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT 0x00000700
#define BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(v) \
(((v) << 8) & BM_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT)
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__0 0x0
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__1 0x1
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__2 0x2
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__3 0x3
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__4 0x4
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__5 0x5
#define BP_PRE_PREFETCH_ENGINE_CTRL_RSVD0 6
#define BM_PRE_PREFETCH_ENGINE_CTRL_RSVD0 0x000000C0
#define BF_PRE_PREFETCH_ENGINE_CTRL_RSVD0(v) \
(((v) << 6) & BM_PRE_PREFETCH_ENGINE_CTRL_RSVD0)
#define BP_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP 4
#define BM_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP 0x00000030
#define BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(v) \
(((v) << 4) & BM_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP)
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP__0 0x0
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP__1 0x1
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP__2 0x2
#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP__3 0x3
#define BP_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES 1
#define BM_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES 0x0000000E
#define BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(v) \
(((v) << 1) & BM_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES)
#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__8_bytes 0x0
#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__16_bytes 0x1
#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__32_bytes 0x2
#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__64_bytes 0x3
#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__128_bytes 0x4
#define BM_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN 0x00000001
#define BF_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN(v) \
(((v) << 0) & BM_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN)
#define BV_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN__0 0x0
#define BV_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN__1 0x1
#define HW_PRE_PREFETCH_ENGINE_STATUS (0x00000090)
#define BP_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_Y 16
#define BM_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_Y 0x3FFF0000
#define BF_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_Y(v) \
(((v) << 16) & BM_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_Y)
#define BP_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_X 0
#define BM_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_X 0x0000FFFF
#define BF_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_X(v) \
(((v) << 0) & BM_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_X)
#define HW_PRE_PREFETCH_ENGINE_INPUT_SIZE (0x000000a0)
#define BP_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT 16
#define BM_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT 0xFFFF0000
#define BF_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT(v) \
(((v) << 16) & BM_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT)
#define BP_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH 0
#define BM_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH 0x0000FFFF
#define BF_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH(v) \
(((v) << 0) & BM_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH)
#define HW_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC (0x000000b0)
#define BP_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y 16
#define BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y 0xFFFF0000
#define BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y(v) \
(((v) << 16) & BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y)
#define BP_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X 0
#define BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X 0x0000FFFF
#define BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X(v) \
(((v) << 0) & BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X)
#define HW_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC (0x000000c0)
#define BP_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_Y 16
#define BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_Y 0xFFFF0000
#define BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_Y(v) \
(((v) << 16) & BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_Y)
#define BP_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_X 0
#define BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_X 0x0000FFFF
#define BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_X(v) \
(((v) << 0) & BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_X)
#define HW_PRE_PREFETCH_ENGINE_PITCH (0x000000d0)
#define BP_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH 16
#define BM_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH 0xFFFF0000
#define BF_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH(v) \
(((v) << 16) & BM_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH)
#define BP_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH 0
#define BM_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH 0x0000FFFF
#define BF_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH(v) \
(((v) << 0) & BM_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH)
#define HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET (0x000000e0)
#define HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_SET (0x000000e4)
#define HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_CLR (0x000000e8)
#define HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_TOG (0x000000ec)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD0 29
#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD0 0xE0000000
#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD0(v) \
(((v) << 29) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD0)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET3 24
#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET3 0x1F000000
#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET3(v) \
(((v) << 24) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET3)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD1 21
#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD1 0x00E00000
#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD1(v) \
(((v) << 21) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD1)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET2 16
#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET2 0x001F0000
#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET2(v) \
(((v) << 16) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET2)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD2 13
#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD2 0x0000E000
#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD2(v) \
(((v) << 13) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD2)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET1 8
#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET1 0x00001F00
#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET1(v) \
(((v) << 8) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET1)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD3 5
#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD3 0x000000E0
#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD3(v) \
(((v) << 5) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD3)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET0 0
#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET0 0x0000001F
#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET0(v) \
(((v) << 0) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET0)
#define HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH (0x000000f0)
#define HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_SET (0x000000f4)
#define HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_CLR (0x000000f8)
#define HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_TOG (0x000000fc)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_RSVD0 16
#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_RSVD0 0xFFFF0000
#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_RSVD0(v) \
(((v) << 16) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_RSVD0)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH3 12
#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH3 0x0000F000
#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH3(v) \
(((v) << 12) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH3)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH2 8
#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH2 0x00000F00
#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH2(v) \
(((v) << 8) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH2)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH1 4
#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH1 0x000000F0
#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH1(v) \
(((v) << 4) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH1)
#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH0 0
#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH0 0x0000000F
#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH0(v) \
(((v) << 0) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH0)
#define HW_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET (0x00000100)
#define BP_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_RSVD0 23
#define BM_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_RSVD0 0xFF800000
#define BF_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_RSVD0(v) \
(((v) << 23) & BM_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_RSVD0)
#define BP_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET 0
#define BM_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET 0xFFFFFFFF
#define BF_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET(v) \
(((v) << 0) & BM_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET)
#define HW_PRE_STORE_ENGINE_CTRL (0x00000110)
#define HW_PRE_STORE_ENGINE_CTRL_SET (0x00000114)
#define HW_PRE_STORE_ENGINE_CTRL_CLR (0x00000118)
#define HW_PRE_STORE_ENGINE_CTRL_TOG (0x0000011c)
#define BP_PRE_STORE_ENGINE_CTRL_RSVD0 6
#define BM_PRE_STORE_ENGINE_CTRL_RSVD0 0xFFFFFFC0
#define BF_PRE_STORE_ENGINE_CTRL_RSVD0(v) \
(((v) << 6) & BM_PRE_STORE_ENGINE_CTRL_RSVD0)
#define BP_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP 4
#define BM_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP 0x00000030
#define BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(v) \
(((v) << 4) & BM_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP)
#define BV_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP__8_bits 0x0
#define BV_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP__16_bits 0x1
#define BV_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP__32_bits 0x2
#define BV_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP__64_bits 0x3
#define BP_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES 1
#define BM_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES 0x0000000E
#define BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(v) \
(((v) << 1) & BM_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES)
#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__8_bytes 0x0
#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__16_bytes 0x1
#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__32_bytes 0x2
#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__64_bytes 0x3
#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__128_bytes 0x4
#define BM_PRE_STORE_ENGINE_CTRL_STORE_EN 0x00000001
#define BF_PRE_STORE_ENGINE_CTRL_STORE_EN(v) \
(((v) << 0) & BM_PRE_STORE_ENGINE_CTRL_STORE_EN)
#define BV_PRE_STORE_ENGINE_CTRL_STORE_EN__0 0x0
#define BV_PRE_STORE_ENGINE_CTRL_STORE_EN__1 0x1
#define HW_PRE_STORE_ENGINE_STATUS (0x00000120)
#define BP_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y 16
#define BM_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y 0x3FFF0000
#define BF_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y(v) \
(((v) << 16) & BM_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y)
#define BP_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_X 0
#define BM_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_X 0x0000FFFF
#define BF_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_X(v) \
(((v) << 0) & BM_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_X)
#define HW_PRE_STORE_ENGINE_SIZE (0x00000130)
#define BP_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT 16
#define BM_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT 0xFFFF0000
#define BF_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT(v) \
(((v) << 16) & BM_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT)
#define BP_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH 0
#define BM_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH 0x0000FFFF
#define BF_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH(v) \
(((v) << 0) & BM_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH)
#define HW_PRE_STORE_ENGINE_PITCH (0x00000140)
#define BP_PRE_STORE_ENGINE_PITCH_RSVD0 16
#define BM_PRE_STORE_ENGINE_PITCH_RSVD0 0xFFFF0000
#define BF_PRE_STORE_ENGINE_PITCH_RSVD0(v) \
(((v) << 16) & BM_PRE_STORE_ENGINE_PITCH_RSVD0)
#define BP_PRE_STORE_ENGINE_PITCH_OUT_PITCH 0
#define BM_PRE_STORE_ENGINE_PITCH_OUT_PITCH 0x0000FFFF
#define BF_PRE_STORE_ENGINE_PITCH_OUT_PITCH(v) \
(((v) << 0) & BM_PRE_STORE_ENGINE_PITCH_OUT_PITCH)
#define HW_PRE_STORE_ENGINE_ADDR (0x00000150)
#define BP_PRE_STORE_ENGINE_ADDR_OUT_BASE_ADDR 0
#define BM_PRE_STORE_ENGINE_ADDR_OUT_BASE_ADDR 0xFFFFFFFF
#define BF_PRE_STORE_ENGINE_ADDR_OUT_BASE_ADDR(v) (v)
#endif /* __ARCH_ARM___PRE_H */

View File

@ -0,0 +1,973 @@
/*
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/clk.h>
#include <linux/genalloc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ipu-v3.h>
#include <linux/ipu-v3-pre.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include "pre-regs.h"
struct ipu_pre_data {
unsigned int id;
struct device *dev;
void __iomem *base;
struct clk *clk;
struct mutex mutex; /* for in_use */
spinlock_t lock; /* for register access */
struct list_head list;
struct gen_pool *iram_pool;
unsigned long double_buffer_size;
unsigned long double_buffer_base;
unsigned long double_buffer_paddr;
bool in_use;
bool enabled;
};
static LIST_HEAD(pre_list);
static DEFINE_MUTEX(pre_list_lock);
static inline void pre_write(struct ipu_pre_data *pre,
u32 value, unsigned int offset)
{
writel(value, pre->base + offset);
}
static inline u32 pre_read(struct ipu_pre_data *pre, unsigned offset)
{
return readl(pre->base + offset);
}
static struct ipu_pre_data *get_pre(unsigned int id)
{
struct ipu_pre_data *pre;
mutex_lock(&pre_list_lock);
list_for_each_entry(pre, &pre_list, list) {
if (pre->id == id) {
mutex_unlock(&pre_list_lock);
return pre;
}
}
mutex_unlock(&pre_list_lock);
return NULL;
}
int ipu_pre_alloc(int ipu_id, ipu_channel_t channel)
{
struct ipu_pre_data *pre;
int i, fixed;
if (channel == MEM_BG_SYNC) {
fixed = ipu_id ? 3 : 0;
pre = get_pre(fixed);
if (pre) {
mutex_lock(&pre->mutex);
if (!pre->in_use) {
pre->in_use = true;
mutex_unlock(&pre->mutex);
return pre->id;
}
mutex_unlock(&pre->mutex);
}
return pre ? -EBUSY : -ENOENT;
}
for (i = 1; i < 3; i++) {
pre = get_pre(i);
if (!pre)
continue;
mutex_lock(&pre->mutex);
if (!pre->in_use) {
pre->in_use = true;
mutex_unlock(&pre->mutex);
return pre->id;
}
mutex_unlock(&pre->mutex);
}
return pre ? -EBUSY : -ENOENT;
}
EXPORT_SYMBOL(ipu_pre_alloc);
void ipu_pre_free(unsigned int *id)
{
struct ipu_pre_data *pre;
pre = get_pre(*id);
if (!pre)
return;
mutex_lock(&pre->mutex);
pre->in_use = false;
mutex_unlock(&pre->mutex);
*id = -1;
}
EXPORT_SYMBOL(ipu_pre_free);
unsigned long ipu_pre_alloc_double_buffer(unsigned int id, unsigned int size)
{
struct ipu_pre_data *pre = get_pre(id);
if (!pre)
return -ENOENT;
if (!size)
return -EINVAL;
pre->double_buffer_base = gen_pool_alloc(pre->iram_pool, size);
if (!pre->double_buffer_base) {
dev_err(pre->dev, "double buffer allocate failed\n");
return -ENOMEM;
}
pre->double_buffer_size = size;
pre->double_buffer_paddr = gen_pool_virt_to_phys(pre->iram_pool,
pre->double_buffer_base);
return pre->double_buffer_paddr;
}
EXPORT_SYMBOL(ipu_pre_alloc_double_buffer);
void ipu_pre_free_double_buffer(unsigned int id)
{
struct ipu_pre_data *pre = get_pre(id);
if (!pre)
return;
if (pre->double_buffer_base) {
gen_pool_free(pre->iram_pool,
pre->double_buffer_base,
pre->double_buffer_size);
pre->double_buffer_base = 0;
pre->double_buffer_size = 0;
pre->double_buffer_paddr = 0;
}
}
EXPORT_SYMBOL(ipu_pre_free_double_buffer);
/* PRE register configurations */
static int ipu_pre_set_ctrl(unsigned int id,
bool repeat,
bool vflip,
bool handshake_en,
bool hsk_abort_en,
unsigned int hsk_line_num,
bool sdw_update,
unsigned int block_size,
unsigned int interlaced,
unsigned int prefetch_mode)
{
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
int ret = 0;
if (!pre)
return -EINVAL;
spin_lock_irqsave(&pre->lock, lock_flags);
pre_write(pre, BF_PRE_CTRL_TPR_RESET_SEL(1), HW_PRE_CTRL_SET);
if (repeat)
pre_write(pre, BF_PRE_CTRL_EN_REPEAT(1), HW_PRE_CTRL_SET);
else
pre_write(pre, BM_PRE_CTRL_EN_REPEAT, HW_PRE_CTRL_CLR);
if (vflip)
pre_write(pre, BF_PRE_CTRL_VFLIP(1), HW_PRE_CTRL_SET);
else
pre_write(pre, BM_PRE_CTRL_VFLIP, HW_PRE_CTRL_CLR);
if (handshake_en) {
pre_write(pre, BF_PRE_CTRL_HANDSHAKE_EN(1), HW_PRE_CTRL_SET);
if (hsk_abort_en)
pre_write(pre, BF_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN(1),
HW_PRE_CTRL_SET);
else
pre_write(pre, BM_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN,
HW_PRE_CTRL_CLR);
switch (hsk_line_num) {
case 0 /* 4 lines */:
pre_write(pre, BM_PRE_CTRL_HANDSHAKE_LINE_NUM,
HW_PRE_CTRL_CLR);
break;
case 1 /* 8 lines */:
pre_write(pre, BM_PRE_CTRL_HANDSHAKE_LINE_NUM,
HW_PRE_CTRL_CLR);
pre_write(pre, BF_PRE_CTRL_HANDSHAKE_LINE_NUM(1),
HW_PRE_CTRL_SET);
break;
case 2 /* 16 lines */:
pre_write(pre, BM_PRE_CTRL_HANDSHAKE_LINE_NUM,
HW_PRE_CTRL_CLR);
pre_write(pre, BF_PRE_CTRL_HANDSHAKE_LINE_NUM(2),
HW_PRE_CTRL_SET);
break;
default:
dev_err(pre->dev, "invalid hanshake line number\n");
ret = -EINVAL;
goto err;
}
} else
pre_write(pre, BM_PRE_CTRL_HANDSHAKE_EN, HW_PRE_CTRL_CLR);
switch (prefetch_mode) {
case 0:
pre_write(pre, BM_PRE_CTRL_BLOCK_EN, HW_PRE_CTRL_CLR);
break;
case 1:
pre_write(pre, BF_PRE_CTRL_BLOCK_EN(1), HW_PRE_CTRL_SET);
switch (block_size) {
case 0:
pre_write(pre, BM_PRE_CTRL_BLOCK_16, HW_PRE_CTRL_CLR);
break;
case 1:
pre_write(pre, BF_PRE_CTRL_BLOCK_16(1), HW_PRE_CTRL_SET);
break;
default:
dev_err(pre->dev, "invalid block size for pre\n");
ret = -EINVAL;
goto err;
}
break;
default:
dev_err(pre->dev, "invalid prefech mode for pre\n");
ret = -EINVAL;
goto err;
}
switch (interlaced) {
case 0: /* progressive mode */
pre_write(pre, BM_PRE_CTRL_SO, HW_PRE_CTRL_CLR);
break;
case 2: /* interlaced mode: Pal */
pre_write(pre, BF_PRE_CTRL_SO(1), HW_PRE_CTRL_SET);
pre_write(pre, BM_PRE_CTRL_INTERLACED_FIELD, HW_PRE_CTRL_CLR);
break;
case 3: /* interlaced mode: NTSC */
pre_write(pre, BF_PRE_CTRL_SO(1), HW_PRE_CTRL_SET);
pre_write(pre, BF_PRE_CTRL_INTERLACED_FIELD(1), HW_PRE_CTRL_SET);
break;
default:
dev_err(pre->dev, "invalid interlaced or progressive mode\n");
ret = -EINVAL;
goto err;
}
if (sdw_update)
pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
else
pre_write(pre, BM_PRE_CTRL_SDW_UPDATE, HW_PRE_CTRL_CLR);
err:
spin_unlock_irqrestore(&pre->lock, lock_flags);
return ret;
}
static void ipu_pre_irq_mask(struct ipu_pre_data *pre,
unsigned long mask, bool clear)
{
if (clear) {
pre_write(pre, mask & 0xf, HW_PRE_IRQ_MASK_CLR);
return;
}
pre_write(pre, mask & 0xf, HW_PRE_IRQ_MASK_SET);
}
static int ipu_pre_buf_set(unsigned int id, unsigned long cur_buf,
unsigned long next_buf)
{
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
if (!pre)
return -EINVAL;
spin_lock_irqsave(&pre->lock, lock_flags);
pre_write(pre, cur_buf, HW_PRE_CUR_BUF);
pre_write(pre, next_buf, HW_PRE_NEXT_BUF);
spin_unlock_irqrestore(&pre->lock, lock_flags);
return 0;
}
static int ipu_pre_plane_buf_off_set(unsigned int id,
unsigned long sec_buf_off,
unsigned long trd_buf_off)
{
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
if (!pre || sec_buf_off & BM_PRE_U_BUF_OFFSET_RSVD0 ||
trd_buf_off & BM_PRE_V_BUF_OFFSET_RSVD0)
return -EINVAL;
spin_lock_irqsave(&pre->lock, lock_flags);
pre_write(pre, sec_buf_off, HW_PRE_U_BUF_OFFSET);
pre_write(pre, trd_buf_off, HW_PRE_V_BUF_OFFSET);
spin_unlock_irqrestore(&pre->lock, lock_flags);
return 0;
}
static int ipu_pre_tpr_set(unsigned int id, unsigned int tile_fmt)
{
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
unsigned int tpr_ctrl, fmt;
if (!pre)
return -EINVAL;
switch (tile_fmt) {
case 0x0: /* Bypass */
fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x0);
break;
case IPU_PIX_FMT_GPU32_SB_ST:
fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x10);
break;
case IPU_PIX_FMT_GPU16_SB_ST:
fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x11);
break;
case IPU_PIX_FMT_GPU32_ST:
fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x20);
break;
case IPU_PIX_FMT_GPU16_ST:
fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x21);
break;
case IPU_PIX_FMT_GPU32_SB_SRT:
fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x50);
break;
case IPU_PIX_FMT_GPU16_SB_SRT:
fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x51);
break;
case IPU_PIX_FMT_GPU32_SRT:
fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x60);
break;
case IPU_PIX_FMT_GPU16_SRT:
fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x61);
break;
default:
dev_err(pre->dev, "invalid tile fmt for pre\n");
return -EINVAL;
}
spin_lock_irqsave(&pre->lock, lock_flags);
tpr_ctrl = pre_read(pre, HW_PRE_TPR_CTRL);
tpr_ctrl &= ~BM_PRE_TPR_CTRL_TILE_FORMAT;
tpr_ctrl |= fmt;
pre_write(pre, tpr_ctrl, HW_PRE_TPR_CTRL);
spin_unlock_irqrestore(&pre->lock, lock_flags);
return 0;
}
static int ipu_pre_set_shift(int id, unsigned int offset, unsigned int width)
{
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
if (!pre)
return -EINVAL;
spin_lock_irqsave(&pre->lock, lock_flags);
pre_write(pre, offset, HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET);
pre_write(pre, width, HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH);
spin_unlock_irqrestore(&pre->lock, lock_flags);
return 0;
}
static int ipu_pre_prefetch(unsigned int id,
unsigned int read_burst,
unsigned int input_bpp,
unsigned int input_pixel_fmt,
bool shift_bypass,
bool field_inverse,
bool tpr_coor_offset_en,
struct ipu_rect output_size,
unsigned int input_width,
unsigned int input_height,
unsigned int input_active_width,
unsigned int interlaced,
int interlace_offset)
{
unsigned int prefetch_ctrl = 0;
unsigned int input_y_pitch = 0, input_uv_pitch = 0;
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
if (!pre)
return -EINVAL;
spin_lock_irqsave(&pre->lock, lock_flags);
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN(1);
switch (read_burst) {
case 0x0:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x0);
break;
case 0x1:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x1);
break;
case 0x2:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x2);
break;
case 0x3:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x3);
break;
case 0x4:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x4);
break;
default:
spin_unlock_irqrestore(&pre->lock, lock_flags);
dev_err(pre->dev, "invalid read burst for prefetch engine\n");
return -EINVAL;
}
switch (input_bpp) {
case 8:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(0x0);
break;
case 16:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(0x1);
break;
case 32:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(0x2);
break;
case 64:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(0x3);
break;
default:
spin_unlock_irqrestore(&pre->lock, lock_flags);
dev_err(pre->dev, "invalid input bpp for prefetch engine\n");
return -EINVAL;
}
switch (input_pixel_fmt) {
case 0x1: /* tile */
case 0x0: /* generic data */
case IPU_PIX_FMT_RGB666:
case IPU_PIX_FMT_RGB565:
case IPU_PIX_FMT_BGRA4444:
case IPU_PIX_FMT_BGRA5551:
case IPU_PIX_FMT_BGR24:
case IPU_PIX_FMT_RGB24:
case IPU_PIX_FMT_GBR24:
case IPU_PIX_FMT_BGR32:
case IPU_PIX_FMT_BGRA32:
case IPU_PIX_FMT_RGB32:
case IPU_PIX_FMT_RGBA32:
case IPU_PIX_FMT_ABGR32:
case IPU_PIX_FMT_YUYV:
case IPU_PIX_FMT_UYVY:
case IPU_PIX_FMT_YUV444:
case IPU_PIX_FMT_AYUV:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x0);
input_y_pitch = input_width * (input_bpp >> 3);
if (interlaced && input_pixel_fmt != 0x1)
input_y_pitch *= 2;
break;
case IPU_PIX_FMT_YUV444P:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x1);
input_y_pitch = input_width;
input_uv_pitch = input_width;
break;
case IPU_PIX_FMT_YUV422P:
case IPU_PIX_FMT_YVU422P:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x2);
input_y_pitch = input_width;
input_uv_pitch = input_width >> 1;
break;
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x3);
input_y_pitch = input_width;
input_uv_pitch = input_width >> 1;
break;
case PRE_PIX_FMT_NV61:
prefetch_ctrl |= BM_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP;
case IPU_PIX_FMT_NV16:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x4);
input_y_pitch = input_width;
input_uv_pitch = input_width;
break;
case PRE_PIX_FMT_NV21:
prefetch_ctrl |= BM_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP;
case IPU_PIX_FMT_NV12:
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x5);
input_y_pitch = input_width;
input_uv_pitch = input_width;
break;
default:
spin_unlock_irqrestore(&pre->lock, lock_flags);
dev_err(pre->dev, "invalid input pixel format for prefetch engine\n");
return -EINVAL;
}
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS(shift_bypass ? 1 : 0);
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE(field_inverse ? 1 : 0);
prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_TPR_COOR_OFFSET_EN(tpr_coor_offset_en ? 1 : 0);
pre_write(pre, BF_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH(input_active_width) |
BF_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT(input_height),
HW_PRE_PREFETCH_ENGINE_INPUT_SIZE);
if (tpr_coor_offset_en)
pre_write(pre, BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X(output_size.left) |
BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y(output_size.top),
HW_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC);
pre_write(pre, BF_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH(input_y_pitch) |
BF_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH(input_uv_pitch),
HW_PRE_PREFETCH_ENGINE_PITCH);
pre_write(pre, BF_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET(interlace_offset), HW_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET);
pre_write(pre, prefetch_ctrl, HW_PRE_PREFETCH_ENGINE_CTRL);
spin_unlock_irqrestore(&pre->lock, lock_flags);
return 0;
}
static int ipu_pre_store(unsigned int id,
bool store_en,
unsigned int write_burst,
unsigned int output_bpp,
/* this means the output
* width by prefetch
*/
unsigned int input_width,
unsigned int input_height,
unsigned int out_pitch,
unsigned int output_addr)
{
struct ipu_pre_data *pre = get_pre(id);
unsigned int store_ctrl = 0;
unsigned long lock_flags;
if (!pre)
return -EINVAL;
spin_lock_irqsave(&pre->lock, lock_flags);
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_STORE_EN(store_en ? 1 : 0);
if (store_en) {
switch (write_burst) {
case 0x0:
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x0);
break;
case 0x1:
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x1);
break;
case 0x2:
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x2);
break;
case 0x3:
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x3);
break;
case 0x4:
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x4);
break;
default:
spin_unlock_irqrestore(&pre->lock, lock_flags);
dev_err(pre->dev, "invalid write burst value for store engine\n");
return -EINVAL;
}
switch (output_bpp) {
case 8:
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(0x0);
break;
case 16:
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(0x1);
break;
case 32:
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(0x2);
break;
case 64:
store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(0x3);
break;
default:
spin_unlock_irqrestore(&pre->lock, lock_flags);
dev_err(pre->dev, "invalid ouput bpp for store engine\n");
return -EINVAL;
}
pre_write(pre, BF_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH(input_width) |
BF_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT(input_height),
HW_PRE_STORE_ENGINE_SIZE);
pre_write(pre, BF_PRE_STORE_ENGINE_PITCH_OUT_PITCH(out_pitch),
HW_PRE_STORE_ENGINE_PITCH);
pre_write(pre, BF_PRE_STORE_ENGINE_ADDR_OUT_BASE_ADDR(output_addr),
HW_PRE_STORE_ENGINE_ADDR);
}
pre_write(pre, store_ctrl, HW_PRE_STORE_ENGINE_CTRL);
spin_unlock_irqrestore(&pre->lock, lock_flags);
return 0;
}
/* End */
static irqreturn_t ipu_pre_irq_handle(int irq, void *dev_id)
{
struct ipu_pre_data *pre = dev_id;
unsigned int irq_stat, axi_id = 0;
spin_lock(&pre->lock);
irq_stat = pre_read(pre, HW_PRE_IRQ);
if (irq_stat & BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ) {
dev_warn(pre->dev, "handshake abort\n");
pre_write(pre, BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ, HW_PRE_IRQ_CLR);
}
if (irq_stat & BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ) {
dev_warn(pre->dev, "tpr read num bytes overflow\n");
pre_write(pre, BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ,
HW_PRE_IRQ_CLR);
}
if (irq_stat & BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ) {
dev_warn(pre->dev, "handshake error\n");
pre_write(pre, BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ, HW_PRE_IRQ_CLR);
}
axi_id = (irq_stat & BM_PRE_IRQ_AXI_ERROR_ID) >>
BP_PRE_IRQ_AXI_ERROR_ID;
if (irq_stat & BM_PRE_IRQ_AXI_WRITE_ERROR) {
dev_warn(pre->dev, "AXI%d write error\n", axi_id);
pre_write(pre, BM_PRE_IRQ_AXI_WRITE_ERROR, HW_PRE_IRQ_CLR);
}
if (irq_stat & BM_PRE_IRQ_AXI_READ_ERROR) {
dev_warn(pre->dev, "AXI%d read error\n", axi_id);
pre_write(pre, BM_PRE_IRQ_AXI_READ_ERROR, HW_PRE_IRQ_CLR);
}
spin_unlock(&pre->lock);
return IRQ_HANDLED;
}
static void ipu_pre_out_of_reset(unsigned int id)
{
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
if (!pre)
return;
spin_lock_irqsave(&pre->lock, lock_flags);
pre_write(pre, BF_PRE_CTRL_SFTRST(1) | BF_PRE_CTRL_CLKGATE(1),
HW_PRE_CTRL_CLR);
spin_unlock_irqrestore(&pre->lock, lock_flags);
}
int ipu_pre_config(int id, struct ipu_pre_context *config)
{
int ret = 0;
struct ipu_pre_data *pre = get_pre(id);
if (!config || !pre)
return -EINVAL;
config->store_addr = pre->double_buffer_paddr;
if (!pre->enabled)
clk_prepare_enable(pre->clk);
ipu_pre_out_of_reset(id);
ret = ipu_pre_plane_buf_off_set(id, config->sec_buf_off,
config->trd_buf_off);
if (ret < 0)
goto out;
ret = ipu_pre_tpr_set(id, config->tile_fmt);
if (ret < 0)
goto out;
ret = ipu_pre_buf_set(id, config->cur_buf, config->next_buf);
if (ret < 0)
goto out;
ret = ipu_pre_set_shift(id, config->prefetch_shift_offset,
config->prefetch_shift_width);
if (ret < 0)
goto out;
ret = ipu_pre_prefetch(id, config->read_burst, config->prefetch_input_bpp,
config->prefetch_input_pixel_fmt, config->shift_bypass,
config->field_inverse, config->tpr_coor_offset_en,
config->prefetch_output_size, config->prefetch_input_width,
config->prefetch_input_height,
config->prefetch_input_active_width,
config->interlaced,
config->interlace_offset);
if (ret < 0)
goto out;
ret = ipu_pre_store(id, config->store_en,
config->write_burst, config->store_output_bpp,
config->prefetch_output_size.width, config->prefetch_output_size.height,
config->store_pitch,
config->store_addr);
if (ret < 0)
goto out;
ret = ipu_pre_set_ctrl(id, config->repeat,
config->vflip, config->handshake_en,
config->hsk_abort_en, config->hsk_line_num,
config->sdw_update, config->block_size,
config->interlaced, config->prefetch_mode);
ipu_pre_irq_mask(pre, BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ |
BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ |
BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ, false);
out:
if (!pre->enabled)
clk_disable_unprepare(pre->clk);
return ret;
}
EXPORT_SYMBOL(ipu_pre_config);
int ipu_pre_enable(int id)
{
int ret = 0;
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
if (!pre)
return -EINVAL;
if (pre->enabled)
return 0;
clk_prepare_enable(pre->clk);
/* start the pre engine */
spin_lock_irqsave(&pre->lock, lock_flags);
pre_write(pre, BF_PRE_CTRL_ENABLE(1), HW_PRE_CTRL_SET);
spin_unlock_irqrestore(&pre->lock, lock_flags);
pre->enabled = true;
return ret;
}
EXPORT_SYMBOL(ipu_pre_enable);
int ipu_pre_sdw_update(int id)
{
int ret = 0;
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
if (!pre)
return -EINVAL;
if (!pre->enabled)
clk_prepare_enable(pre->clk);
/* start the pre engine */
spin_lock_irqsave(&pre->lock, lock_flags);
pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
spin_unlock_irqrestore(&pre->lock, lock_flags);
if (!pre->enabled)
clk_disable_unprepare(pre->clk);
return ret;
}
EXPORT_SYMBOL(ipu_pre_sdw_update);
void ipu_pre_disable(int id)
{
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
if (!pre)
return;
if (!pre->enabled)
return;
/* stop the pre engine */
spin_lock_irqsave(&pre->lock, lock_flags);
pre_write(pre, BF_PRE_CTRL_ENABLE(1), HW_PRE_CTRL_CLR);
pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
pre_write(pre, BF_PRE_CTRL_SFTRST(1), HW_PRE_CTRL_SET);
spin_unlock_irqrestore(&pre->lock, lock_flags);
clk_disable_unprepare(pre->clk);
pre->enabled = false;
}
EXPORT_SYMBOL(ipu_pre_disable);
int ipu_pre_set_fb_buffer(int id, unsigned long fb_paddr,
unsigned int x_crop,
unsigned int y_crop,
unsigned int sec_buf_off,
unsigned int trd_buf_off)
{
struct ipu_pre_data *pre = get_pre(id);
unsigned long lock_flags;
if (!pre)
return -EINVAL;
spin_lock_irqsave(&pre->lock, lock_flags);
pre_write(pre, fb_paddr, HW_PRE_NEXT_BUF);
pre_write(pre, sec_buf_off, HW_PRE_U_BUF_OFFSET);
pre_write(pre, trd_buf_off, HW_PRE_V_BUF_OFFSET);
pre_write(pre, BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X(x_crop) |
BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y(y_crop),
HW_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC);
pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
spin_unlock_irqrestore(&pre->lock, lock_flags);
return 0;
}
EXPORT_SYMBOL(ipu_pre_set_fb_buffer);
static int ipu_pre_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct ipu_pre_data *pre;
struct resource *res;
int id, irq, err;
pre = devm_kzalloc(&pdev->dev, sizeof(*pre), GFP_KERNEL);
if (!pre)
return -ENOMEM;
pre->dev = &pdev->dev;
id = of_alias_get_id(np, "pre");
if (id < 0) {
dev_err(&pdev->dev, "failed to get PRE id\n");
return id;
}
pre->id = id;
mutex_init(&pre->mutex);
spin_lock_init(&pre->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pre->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pre->base))
return PTR_ERR(pre->base);
pre->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pre->clk)) {
dev_err(&pdev->dev, "failed to get the pre clk\n");
return PTR_ERR(pre->clk);
}
irq = platform_get_irq(pdev, 0);
err = devm_request_irq(&pdev->dev, irq, ipu_pre_irq_handle,
IRQF_TRIGGER_RISING, pdev->name, pre);
if (err) {
dev_err(&pdev->dev, "failed to request pre irq\n");
return err;
}
pre->iram_pool = of_gen_pool_get(pdev->dev.of_node, "ocram", 0);
if (!pre->iram_pool) {
dev_err(&pdev->dev, "no iram exist for pre\n");
return -ENOMEM;
}
mutex_lock(&pre_list_lock);
list_add_tail(&pre->list, &pre_list);
mutex_unlock(&pre_list_lock);
ipu_pre_alloc_double_buffer(pre->id, IPU_PRE_MAX_WIDTH * 8 * IPU_PRE_MAX_BPP);
/* PRE GATE ON */
clk_prepare_enable(pre->clk);
pre_write(pre, BF_PRE_CTRL_SFTRST(1) | BF_PRE_CTRL_CLKGATE(1),
HW_PRE_CTRL_CLR);
pre_write(pre, 0xf, HW_PRE_IRQ_MASK);
clk_disable_unprepare(pre->clk);
platform_set_drvdata(pdev, pre);
dev_info(&pdev->dev, "driver probed\n");
return 0;
}
static int ipu_pre_remove(struct platform_device *pdev)
{
struct ipu_pre_data *pre = platform_get_drvdata(pdev);
if (pre->iram_pool && pre->double_buffer_base) {
gen_pool_free(pre->iram_pool,
pre->double_buffer_base,
pre->double_buffer_size);
}
mutex_lock(&pre_list_lock);
list_del(&pre->list);
mutex_unlock(&pre_list_lock);
return 0;
}
static const struct of_device_id imx_ipu_pre_dt_ids[] = {
{ .compatible = "fsl,imx6q-pre", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_ipu_pre_dt_ids);
static struct platform_driver ipu_pre_driver = {
.driver = {
.name = "imx-pre",
.of_match_table = of_match_ptr(imx_ipu_pre_dt_ids),
},
.probe = ipu_pre_probe,
.remove = ipu_pre_remove,
};
static int __init ipu_pre_init(void)
{
return platform_driver_register(&ipu_pre_driver);
}
subsys_initcall(ipu_pre_init);
static void __exit ipu_pre_exit(void)
{
platform_driver_unregister(&ipu_pre_driver);
}
module_exit(ipu_pre_exit);
MODULE_DESCRIPTION("i.MX PRE driver");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,70 @@
/*
* Freescale IPU PRG Register Definitions
*
* Copyright 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/bitops.h>
#ifndef __IPU_PRG_H__
#define __IPU_PRG_H__
#define IPU_PR_CTRL 0x00
#define IPU_PR_STATUS 0x04
#define IPU_PR_QOS 0x08
#define IPU_PR_REG_UPDATE 0x0c
#define IPU_PR_STRIDE(ch) (0x10 + (ch) * 4)
#define IPU_PR_CROP_LINE 0x1c
#define IPU_PR_ADDR_THD 0x20
#define IPU_PR_CH_BADDR(ch) (0x24 + (ch) * 4)
#define IPU_PR_CH_OFFSET(ch) (0x30 + (ch) * 4)
#define IPU_PR_CH_ILO(ch) (0x3c + (ch) * 4)
#define IPU_PR_CH_HEIGHT(ch) (0x48 + (ch) * 4)
#define IPU_PR_CTRL_CH_BYPASS(ch) (0x1 << (ch))
#define IPU_PR_CTRL_SOFT_CH_ARID(ch, n) ((n) << ((ch) * 2 + 8))
#define IPU_PR_CTRL_SOFT_CH_ARID_MASK(ch) (0x3 << ((ch) * 2 + 8))
#define IPU_PR_CTRL_CH_SO(ch, interlace) ((interlace) << ((ch) + 16))
#define IPU_PR_CTRL_CH_SO_MASK(ch) (0x1 << ((ch) + 16))
#define IPU_PR_CTRL_CH_VFLIP(ch, vflip) ((vflip) << ((ch) + 19))
#define IPU_PR_CTRL_CH_VFLIP_MASK(ch) (0x1 << ((ch) + 19))
#define IPU_PR_CTRL_CH_BLOCK_MODE(ch, mode) ((mode) << ((ch) + 22))
#define IPU_PR_CTRL_CH_BLOCK_MODE_MASK(ch) (0x1 << ((ch) + 22))
#define IPU_PR_CTRL_CH_CNT_LOAD_EN(ch) (0x1 << ((ch) + 25))
#define IPU_PR_CTRL_CH_CNT_LOAD_EN_MASK (0x7 << 25)
#define IPU_PR_CTRL_SOFTRST BIT(30)
#define IPU_PR_CTRL_SHADOW_EN BIT(31)
#define IPU_PR_STATUS_BUF_RDY(ch, buf) (1 << ((ch) * 2 + (buf)))
#define IPU_PR_QOS_PRI(id, qos) ((qos) << ((id) * 4))
#define IPU_PR_QOS_MASK(id) (0xf << ((id) * 4))
#define IPU_PR_REG_UPDATE_EN BIT(0)
#define IPU_PR_STRIDE_MASK 0x3fff
#define IPU_PR_CROP_LINE_NUM(ch, n) ((n) << ((ch) * 4))
#define IPU_PR_CROP_LINE_MASK(ch) (0xf << ((ch) * 4))
#define IPU_PR_ADDR_THD_MASK 0xffffffff
#define IPU_PR_CH_BADDR_MASK 0xffffffff
#define IPU_PR_CH_OFFSET_MASK 0xffffffff
#define IPU_PR_CH_ILO_MASK 0x007fffff
#define IPU_PR_CH_ILO_NUM(ilo) ((ilo) & IPU_PR_CH_ILO_MASK)
#define IPU_PR_CH_HEIGHT_MASK 0x00000fff
#define IPU_PR_CH_HEIGHT_NUM(fh) (((fh) - 1) & IPU_PR_CH_HEIGHT_MASK)
#define IPU_PR_CH_IPU_HEIGHT_MASK 0x0fff0000
#define IPU_PR_CH_IPU_HEIGHT_NUM(fh) ((((fh) - 1) << 16) & IPU_PR_CH_IPU_HEIGHT_MASK)
#endif /* __IPU_PRG_H__ */

View File

@ -0,0 +1,506 @@
/*
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ipu-v3-prg.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "prg-regs.h"
#define PRG_CHAN_NUM 3
struct prg_chan {
unsigned int pre_num;
struct mutex mutex; /* for in_use */
bool in_use;
};
struct ipu_prg_data {
unsigned int id;
void __iomem *base;
unsigned long memory;
struct clk *axi_clk;
struct clk *apb_clk;
struct list_head list;
struct device *dev;
struct prg_chan chan[PRG_CHAN_NUM];
struct regmap *regmap;
struct regmap_field *pre_prg_sel[2];
spinlock_t lock;
};
static LIST_HEAD(prg_list);
static DEFINE_MUTEX(prg_lock);
static inline void prg_write(struct ipu_prg_data *prg,
u32 value, unsigned int offset)
{
writel(value, prg->base + offset);
}
static inline u32 prg_read(struct ipu_prg_data *prg, unsigned offset)
{
return readl(prg->base + offset);
}
static struct ipu_prg_data *get_prg(unsigned int ipu_id)
{
struct ipu_prg_data *prg;
mutex_lock(&prg_lock);
list_for_each_entry(prg, &prg_list, list) {
if (prg->id == ipu_id) {
mutex_unlock(&prg_lock);
return prg;
}
}
mutex_unlock(&prg_lock);
return NULL;
}
static int assign_prg_chan(struct ipu_prg_data *prg, unsigned int pre_num,
ipu_channel_t ipu_ch)
{
int prg_ch;
if (!prg)
return -EINVAL;
switch (ipu_ch) {
case MEM_BG_SYNC:
prg_ch = 0;
break;
case MEM_FG_SYNC:
prg_ch = 1;
break;
case MEM_DC_SYNC:
prg_ch = 2;
break;
default:
dev_err(prg->dev, "wrong ipu channel type\n");
return -EINVAL;
}
mutex_lock(&prg->chan[prg_ch].mutex);
if (!prg->chan[prg_ch].in_use) {
prg->chan[prg_ch].in_use = true;
prg->chan[prg_ch].pre_num = pre_num;
if (prg_ch != 0) {
unsigned int pmux, psel; /* primary */
unsigned int smux, ssel; /* secondary */
struct regmap_field *pfield, *sfield;
psel = pre_num - 1;
ssel = psel ? 0 : 1;
pfield = prg->pre_prg_sel[psel];
sfield = prg->pre_prg_sel[ssel];
pmux = (prg_ch - 1) + (prg->id << 1);
mutex_lock(&prg_lock);
regmap_field_write(pfield, pmux);
/*
* PRE1 and PRE2 cannot bind with a same channel of
* one PRG even if one of the two PREs is disabled.
*/
regmap_field_read(sfield, &smux);
if (smux == pmux) {
smux = pmux ^ 0x1;
regmap_field_write(sfield, smux);
}
mutex_unlock(&prg_lock);
}
mutex_unlock(&prg->chan[prg_ch].mutex);
dev_dbg(prg->dev, "bind prg%u ch%d with pre%u\n",
prg->id, prg_ch, pre_num);
return prg_ch;
}
mutex_unlock(&prg->chan[prg_ch].mutex);
return -EBUSY;
}
static inline int get_prg_chan(struct ipu_prg_data *prg, unsigned int pre_num)
{
int i;
if (!prg)
return -EINVAL;
for (i = 0; i < PRG_CHAN_NUM; i++) {
mutex_lock(&prg->chan[i].mutex);
if (prg->chan[i].in_use &&
prg->chan[i].pre_num == pre_num) {
mutex_unlock(&prg->chan[i].mutex);
return i;
}
mutex_unlock(&prg->chan[i].mutex);
}
return -ENOENT;
}
int ipu_prg_config(struct ipu_prg_config *config)
{
struct ipu_prg_data *prg = get_prg(config->id);
struct ipu_soc *ipu = ipu_get_soc(config->id);
int prg_ch, axi_id;
u32 reg;
if (!prg || config->crop_line > 3 || !ipu)
return -EINVAL;
if (config->height & ~IPU_PR_CH_HEIGHT_MASK)
return -EINVAL;
prg_ch = assign_prg_chan(prg, config->pre_num, config->ipu_ch);
if (prg_ch < 0)
return prg_ch;
axi_id = ipu_ch_param_get_axi_id(ipu, config->ipu_ch, IPU_INPUT_BUFFER);
clk_prepare_enable(prg->axi_clk);
clk_prepare_enable(prg->apb_clk);
spin_lock(&prg->lock);
/* clear all load enable to impact other channels */
reg = prg_read(prg, IPU_PR_CTRL);
reg &= ~IPU_PR_CTRL_CH_CNT_LOAD_EN_MASK;
prg_write(prg, reg, IPU_PR_CTRL);
/* counter load enable */
reg = prg_read(prg, IPU_PR_CTRL);
reg |= IPU_PR_CTRL_CH_CNT_LOAD_EN(prg_ch);
prg_write(prg, reg, IPU_PR_CTRL);
/* AXI ID */
reg = prg_read(prg, IPU_PR_CTRL);
reg &= ~IPU_PR_CTRL_SOFT_CH_ARID_MASK(prg_ch);
reg |= IPU_PR_CTRL_SOFT_CH_ARID(prg_ch, axi_id);
prg_write(prg, reg, IPU_PR_CTRL);
/* so */
reg = prg_read(prg, IPU_PR_CTRL);
reg &= ~IPU_PR_CTRL_CH_SO_MASK(prg_ch);
reg |= IPU_PR_CTRL_CH_SO(prg_ch, config->so);
prg_write(prg, reg, IPU_PR_CTRL);
/* vflip */
reg = prg_read(prg, IPU_PR_CTRL);
reg &= ~IPU_PR_CTRL_CH_VFLIP_MASK(prg_ch);
reg |= IPU_PR_CTRL_CH_VFLIP(prg_ch, config->vflip);
prg_write(prg, reg, IPU_PR_CTRL);
/* block mode */
reg = prg_read(prg, IPU_PR_CTRL);
reg &= ~IPU_PR_CTRL_CH_BLOCK_MODE_MASK(prg_ch);
reg |= IPU_PR_CTRL_CH_BLOCK_MODE(prg_ch, config->block_mode);
prg_write(prg, reg, IPU_PR_CTRL);
/* disable bypass */
reg = prg_read(prg, IPU_PR_CTRL);
reg &= ~IPU_PR_CTRL_CH_BYPASS(prg_ch);
prg_write(prg, reg, IPU_PR_CTRL);
/* stride */
reg = prg_read(prg, IPU_PR_STRIDE(prg_ch));
reg &= ~IPU_PR_STRIDE_MASK;
reg |= config->stride - 1;
prg_write(prg, reg, IPU_PR_STRIDE(prg_ch));
/* ilo */
reg = prg_read(prg, IPU_PR_CH_ILO(prg_ch));
reg &= ~IPU_PR_CH_ILO_MASK;
reg |= IPU_PR_CH_ILO_NUM(config->ilo);
prg_write(prg, reg, IPU_PR_CH_ILO(prg_ch));
/* height */
reg = prg_read(prg, IPU_PR_CH_HEIGHT(prg_ch));
reg &= ~IPU_PR_CH_HEIGHT_MASK;
reg |= IPU_PR_CH_HEIGHT_NUM(config->height);
prg_write(prg, reg, IPU_PR_CH_HEIGHT(prg_ch));
/* ipu height */
reg = prg_read(prg, IPU_PR_CH_HEIGHT(prg_ch));
reg &= ~IPU_PR_CH_IPU_HEIGHT_MASK;
reg |= IPU_PR_CH_IPU_HEIGHT_NUM(config->ipu_height);
prg_write(prg, reg, IPU_PR_CH_HEIGHT(prg_ch));
/* crop */
reg = prg_read(prg, IPU_PR_CROP_LINE);
reg &= ~IPU_PR_CROP_LINE_MASK(prg_ch);
reg |= IPU_PR_CROP_LINE_NUM(prg_ch, config->crop_line);
prg_write(prg, reg, IPU_PR_CROP_LINE);
/* buffer address */
reg = prg_read(prg, IPU_PR_CH_BADDR(prg_ch));
reg &= ~IPU_PR_CH_BADDR_MASK;
reg |= config->baddr;
prg_write(prg, reg, IPU_PR_CH_BADDR(prg_ch));
/* offset */
reg = prg_read(prg, IPU_PR_CH_OFFSET(prg_ch));
reg &= ~IPU_PR_CH_OFFSET_MASK;
reg |= config->offset;
prg_write(prg, reg, IPU_PR_CH_OFFSET(prg_ch));
/* threshold */
reg = prg_read(prg, IPU_PR_ADDR_THD);
reg &= ~IPU_PR_ADDR_THD_MASK;
reg |= prg->memory;
prg_write(prg, reg, IPU_PR_ADDR_THD);
/* shadow enable */
reg = prg_read(prg, IPU_PR_CTRL);
reg |= IPU_PR_CTRL_SHADOW_EN;
prg_write(prg, reg, IPU_PR_CTRL);
/* register update */
reg = prg_read(prg, IPU_PR_REG_UPDATE);
reg |= IPU_PR_REG_UPDATE_EN;
prg_write(prg, reg, IPU_PR_REG_UPDATE);
spin_unlock(&prg->lock);
clk_disable_unprepare(prg->apb_clk);
return 0;
}
EXPORT_SYMBOL(ipu_prg_config);
int ipu_prg_disable(unsigned int ipu_id, unsigned int pre_num)
{
struct ipu_prg_data *prg = get_prg(ipu_id);
int prg_ch;
u32 reg;
if (!prg)
return -EINVAL;
prg_ch = get_prg_chan(prg, pre_num);
if (prg_ch < 0)
return prg_ch;
clk_prepare_enable(prg->apb_clk);
spin_lock(&prg->lock);
/* clear all load enable to impact other channels */
reg = prg_read(prg, IPU_PR_CTRL);
reg &= ~IPU_PR_CTRL_CH_CNT_LOAD_EN_MASK;
prg_write(prg, reg, IPU_PR_CTRL);
/* counter load enable */
reg = prg_read(prg, IPU_PR_CTRL);
reg |= IPU_PR_CTRL_CH_CNT_LOAD_EN(prg_ch);
prg_write(prg, reg, IPU_PR_CTRL);
/* enable bypass */
reg = prg_read(prg, IPU_PR_CTRL);
reg |= IPU_PR_CTRL_CH_BYPASS(prg_ch);
prg_write(prg, reg, IPU_PR_CTRL);
/* shadow enable */
reg = prg_read(prg, IPU_PR_CTRL);
reg |= IPU_PR_CTRL_SHADOW_EN;
prg_write(prg, reg, IPU_PR_CTRL);
/* register update */
reg = prg_read(prg, IPU_PR_REG_UPDATE);
reg |= IPU_PR_REG_UPDATE_EN;
prg_write(prg, reg, IPU_PR_REG_UPDATE);
spin_unlock(&prg->lock);
clk_disable_unprepare(prg->apb_clk);
clk_disable_unprepare(prg->axi_clk);
mutex_lock(&prg->chan[prg_ch].mutex);
prg->chan[prg_ch].in_use = false;
mutex_unlock(&prg->chan[prg_ch].mutex);
return 0;
}
EXPORT_SYMBOL(ipu_prg_disable);
int ipu_prg_wait_buf_ready(unsigned int ipu_id, unsigned int pre_num,
unsigned int hsk_line_num,
int pre_store_out_height)
{
struct ipu_prg_data *prg = get_prg(ipu_id);
int prg_ch, timeout = 1000;
u32 reg;
if (!prg)
return -EINVAL;
prg_ch = get_prg_chan(prg, pre_num);
if (prg_ch < 0)
return prg_ch;
clk_prepare_enable(prg->apb_clk);
spin_lock(&prg->lock);
if (pre_store_out_height <= (4 << hsk_line_num)) {
do {
reg = prg_read(prg, IPU_PR_STATUS);
udelay(1000);
timeout--;
} while (!(reg & IPU_PR_STATUS_BUF_RDY(prg_ch, 0)) && timeout);
} else {
do {
reg = prg_read(prg, IPU_PR_STATUS);
udelay(1000);
timeout--;
} while ((!(reg & IPU_PR_STATUS_BUF_RDY(prg_ch, 0)) ||
!(reg & IPU_PR_STATUS_BUF_RDY(prg_ch, 1))) && timeout);
}
spin_unlock(&prg->lock);
clk_disable_unprepare(prg->apb_clk);
if (!timeout)
dev_err(prg->dev, "wait for buffer ready timeout\n");
return 0;
}
EXPORT_SYMBOL(ipu_prg_wait_buf_ready);
static int ipu_prg_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node, *memory;
struct ipu_prg_data *prg;
struct resource *res;
struct reg_field reg_field0 = REG_FIELD(IOMUXC_GPR5,
IMX6Q_GPR5_PRE_PRG_SEL0_LSB,
IMX6Q_GPR5_PRE_PRG_SEL0_MSB);
struct reg_field reg_field1 = REG_FIELD(IOMUXC_GPR5,
IMX6Q_GPR5_PRE_PRG_SEL1_LSB,
IMX6Q_GPR5_PRE_PRG_SEL1_MSB);
int id, i;
prg = devm_kzalloc(&pdev->dev, sizeof(*prg), GFP_KERNEL);
if (!prg)
return -ENOMEM;
prg->dev = &pdev->dev;
for (i = 0; i < PRG_CHAN_NUM; i++)
mutex_init(&prg->chan[i].mutex);
spin_lock_init(&prg->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
prg->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(prg->base))
return PTR_ERR(prg->base);
prg->axi_clk = devm_clk_get(&pdev->dev, "axi");
if (IS_ERR(prg->axi_clk)) {
dev_err(&pdev->dev, "failed to get the axi clk\n");
return PTR_ERR(prg->axi_clk);
}
prg->apb_clk = devm_clk_get(&pdev->dev, "apb");
if (IS_ERR(prg->apb_clk)) {
dev_err(&pdev->dev, "failed to get the apb clk\n");
return PTR_ERR(prg->apb_clk);
}
prg->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
if (IS_ERR(prg->regmap)) {
dev_err(&pdev->dev, "failed to get regmap\n");
return PTR_ERR(prg->regmap);
}
prg->pre_prg_sel[0] = devm_regmap_field_alloc(&pdev->dev, prg->regmap,
reg_field0);
if (IS_ERR(prg->pre_prg_sel[0]))
return PTR_ERR(prg->pre_prg_sel[0]);
prg->pre_prg_sel[1] = devm_regmap_field_alloc(&pdev->dev, prg->regmap,
reg_field1);
if (IS_ERR(prg->pre_prg_sel[1]))
return PTR_ERR(prg->pre_prg_sel[1]);
memory = of_parse_phandle(np, "memory-region", 0);
if (!memory)
return -ENODEV;
prg->memory = of_translate_address(memory,
of_get_address(memory, 0, NULL, NULL));
id = of_alias_get_id(np, "prg");
if (id < 0) {
dev_err(&pdev->dev, "failed to get PRG id\n");
return id;
}
prg->id = id;
mutex_lock(&prg_lock);
list_add_tail(&prg->list, &prg_list);
mutex_unlock(&prg_lock);
platform_set_drvdata(pdev, prg);
dev_info(&pdev->dev, "driver probed\n");
return 0;
}
static int ipu_prg_remove(struct platform_device *pdev)
{
struct ipu_prg_data *prg = platform_get_drvdata(pdev);
mutex_lock(&prg_lock);
list_del(&prg->list);
mutex_unlock(&prg_lock);
return 0;
}
static const struct of_device_id imx_ipu_prg_dt_ids[] = {
{ .compatible = "fsl,imx6q-prg", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_ipu_prg_dt_ids);
static struct platform_driver ipu_prg_driver = {
.driver = {
.name = "imx-prg",
.of_match_table = of_match_ptr(imx_ipu_prg_dt_ids),
},
.probe = ipu_prg_probe,
.remove = ipu_prg_remove,
};
static int __init ipu_prg_init(void)
{
return platform_driver_register(&ipu_prg_driver);
}
subsys_initcall(ipu_prg_init);
static void __exit ipu_prg_exit(void)
{
platform_driver_unregister(&ipu_prg_driver);
}
module_exit(ipu_prg_exit);
MODULE_DESCRIPTION("i.MX PRG driver");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,536 @@
/*
* Copyright (C) 2012-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/ipu.h>
#include <linux/genalloc.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "vdoa.h"
/* 6band(3field* double buffer) * (width*2) * bandline(8)
= 6x1024x2x8 = 96k or 72k(1.5byte) */
#define MAX_VDOA_IRAM_SIZE (1024*96)
#define VDOA_IRAM_SIZE (1024*72)
#define VDOAC_BAND_HEIGHT_32LINES (32)
#define VDOAC_BAND_HEIGHT_16LINES (16)
#define VDOAC_BAND_HEIGHT_8LINES (8)
#define VDOAC_THREE_FRAMES (0x1 << 2)
#define VDOAC_SYNC_BAND_MODE (0x1 << 3)
#define VDOAC_SCAN_ORDER_INTERLACED (0x1 << 4)
#define VDOAC_PFS_YUYV (0x1 << 5)
#define VDOAC_IPU_SEL_1 (0x1 << 6)
#define VDOAFP_FH_MASK (0x1FFF)
#define VDOAFP_FH_SHIFT (16)
#define VDOAFP_FW_MASK (0x3FFF)
#define VDOAFP_FW_SHIFT (0)
#define VDOASL_VSLY_MASK (0x3FFF)
#define VDOASL_VSLY_SHIFT (16)
#define VDOASL_ISLY_MASK (0x7FFF)
#define VDOASL_ISLY_SHIFT (0)
#define VDOASRR_START_XFER (0x2)
#define VDOASRR_SWRST (0x1)
#define VDOAIEIST_TRANSFER_ERR (0x2)
#define VDOAIEIST_TRANSFER_END (0x1)
#define VDOAC (0x0) /* Control Register */
#define VDOASRR (0x4) /* Start and Reset Register */
#define VDOAIE (0x8) /* Interrupt Enable Register */
#define VDOAIST (0xc) /* Interrupt Status Register */
#define VDOAFP (0x10) /* Frame Parameters Register */
#define VDOAIEBA00 (0x14) /* External Buffer n Frame m Address Register */
#define VDOAIEBA01 (0x18) /* External Buffer n Frame m Address Register */
#define VDOAIEBA02 (0x1c) /* External Buffer n Frame m Address Register */
#define VDOAIEBA10 (0x20) /* External Buffer n Frame m Address Register */
#define VDOAIEBA11 (0x24) /* External Buffer n Frame m Address Register */
#define VDOAIEBA12 (0x28) /* External Buffer n Frame m Address Register */
#define VDOASL (0x2c) /* IPU Stride Line Register */
#define VDOAIUBO (0x30) /* IPU Chroma Buffer Offset Register */
#define VDOAVEBA0 (0x34) /* External Buffer m Address Register */
#define VDOAVEBA1 (0x38) /* External Buffer m Address Register */
#define VDOAVEBA2 (0x3c) /* External Buffer m Address Register */
#define VDOAVUBO (0x40) /* VPU Chroma Buffer Offset */
#define VDOASR (0x44) /* Status Register */
#define VDOATD (0x48) /* Test Debug Register */
enum {
VDOA_INIT = 0x1,
VDOA_GET = 0x2,
VDOA_SETUP = 0x4,
VDOA_GET_OBUF = 0x8,
VDOA_START = 0x10,
VDOA_INIRQ = 0x20,
VDOA_STOP = 0x40,
VDOA_PUT = VDOA_INIT,
};
enum {
VDOA_NULL = 0,
VDOA_FRAME = 1,
VDOA_PREV_FIELD = 2,
VDOA_CURR_FIELD = 3,
VDOA_NEXT_FIELD = 4,
};
#define CHECK_STATE(expect, retcode) \
do { \
if (!((expect) & vdoa->state)) { \
dev_err(vdoa->dev, "ERR: %s state:0x%x, expect:0x%x.\n",\
__func__, vdoa->state, (expect)); \
retcode; \
} \
} while (0)
#define CHECK_NULL_PTR(ptr) \
do { \
pr_debug("vdoa_ptr:0x%p in %s state:0x%x.\n", \
vdoa, __func__, vdoa->state); \
if (NULL == (ptr)) { \
pr_err("ERR vdoa: %s state:0x%x null ptr.\n", \
__func__, vdoa->state); \
} \
} while (0)
struct vdoa_info {
int state;
struct device *dev;
struct clk *vdoa_clk;
void __iomem *reg_base;
struct gen_pool *iram_pool;
unsigned long iram_base;
unsigned long iram_paddr;
int irq;
int field;
struct completion comp;
};
static struct vdoa_info *g_vdoa;
static unsigned long iram_size;
static DEFINE_MUTEX(vdoa_lock);
static inline void vdoa_read_register(struct vdoa_info *vdoa,
u32 reg, u32 *val)
{
*val = ioread32(vdoa->reg_base + reg);
dev_dbg(vdoa->dev, "read_reg:0x%02x, val:0x%08x.\n", reg, *val);
}
static inline void vdoa_write_register(struct vdoa_info *vdoa,
u32 reg, u32 val)
{
iowrite32(val, vdoa->reg_base + reg);
dev_dbg(vdoa->dev, "\t\twrite_reg:0x%02x, val:0x%08x.\n", reg, val);
}
static void dump_registers(struct vdoa_info *vdoa)
{
int i;
u32 data;
for (i = VDOAC; i < VDOATD; i += 4)
vdoa_read_register(vdoa, i, &data);
}
int vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params)
{
int band_size;
int total_band_size = 0;
int ipu_stride;
u32 data;
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
CHECK_NULL_PTR(vdoa);
CHECK_STATE(VDOA_GET | VDOA_GET_OBUF | VDOA_STOP, return -EINVAL);
if (VDOA_GET == vdoa->state) {
dev_dbg(vdoa->dev, "w:%d, h:%d.\n",
params->width, params->height);
data = (params->band_lines == VDOAC_BAND_HEIGHT_32LINES) ? 2 :
((params->band_lines == VDOAC_BAND_HEIGHT_16LINES) ?
1 : 0);
data |= params->scan_order ? VDOAC_SCAN_ORDER_INTERLACED : 0;
data |= params->band_mode ? VDOAC_SYNC_BAND_MODE : 0;
data |= params->pfs ? VDOAC_PFS_YUYV : 0;
data |= params->ipu_num ? VDOAC_IPU_SEL_1 : 0;
vdoa_write_register(vdoa, VDOAC, data);
data = ((params->width & VDOAFP_FW_MASK) << VDOAFP_FW_SHIFT) |
((params->height & VDOAFP_FH_MASK) << VDOAFP_FH_SHIFT);
vdoa_write_register(vdoa, VDOAFP, data);
ipu_stride = params->pfs ? params->width << 1 : params->width;
data = ((params->vpu_stride & VDOASL_VSLY_MASK) <<
VDOASL_VSLY_SHIFT) |
((ipu_stride & VDOASL_ISLY_MASK) << VDOASL_ISLY_SHIFT);
vdoa_write_register(vdoa, VDOASL, data);
dev_dbg(vdoa->dev, "band_mode:%d, band_line:%d, base:0x%lx.\n",
params->band_mode, params->band_lines, vdoa->iram_paddr);
}
/*
* band size = (luma_per_line + chroma_per_line) * bandLines
* = width * (3/2 or 2) * bandLines
* double buffer mode used.
*/
if (params->pfs)
band_size = (params->width << 1) * params->band_lines;
else
band_size = ((params->width * 3) >> 1) *
params->band_lines;
if (params->interlaced) {
total_band_size = 6 * band_size; /* 3 frames*double buffer */
if (iram_size < total_band_size) {
dev_err(vdoa->dev, "iram_size:0x%lx is smaller than "
"request:0x%x!\n", iram_size, total_band_size);
return -EINVAL;
}
if (params->vfield_buf.prev_veba) {
if (params->band_mode) {
vdoa_write_register(vdoa, VDOAIEBA00,
vdoa->iram_paddr);
vdoa_write_register(vdoa, VDOAIEBA10,
vdoa->iram_paddr + band_size);
} else
vdoa_write_register(vdoa, VDOAIEBA00,
params->ieba0);
vdoa_write_register(vdoa, VDOAVEBA0,
params->vfield_buf.prev_veba);
vdoa->field = VDOA_PREV_FIELD;
}
if (params->vfield_buf.cur_veba) {
if (params->band_mode) {
vdoa_write_register(vdoa, VDOAIEBA01,
vdoa->iram_paddr + band_size * 2);
vdoa_write_register(vdoa, VDOAIEBA11,
vdoa->iram_paddr + band_size * 3);
} else
vdoa_write_register(vdoa, VDOAIEBA01,
params->ieba1);
vdoa_write_register(vdoa, VDOAVEBA1,
params->vfield_buf.cur_veba);
vdoa->field = VDOA_CURR_FIELD;
}
if (params->vfield_buf.next_veba) {
if (params->band_mode) {
vdoa_write_register(vdoa, VDOAIEBA02,
vdoa->iram_paddr + band_size * 4);
vdoa_write_register(vdoa, VDOAIEBA12,
vdoa->iram_paddr + band_size * 5);
} else
vdoa_write_register(vdoa, VDOAIEBA02,
params->ieba2);
vdoa_write_register(vdoa, VDOAVEBA2,
params->vfield_buf.next_veba);
vdoa->field = VDOA_NEXT_FIELD;
vdoa_read_register(vdoa, VDOAC, &data);
data |= VDOAC_THREE_FRAMES;
vdoa_write_register(vdoa, VDOAC, data);
}
if (!params->pfs)
vdoa_write_register(vdoa, VDOAIUBO,
params->width * params->band_lines);
vdoa_write_register(vdoa, VDOAVUBO,
params->vfield_buf.vubo);
dev_dbg(vdoa->dev, "total band_size:0x%x.\n", band_size*6);
} else if (params->band_mode) {
/* used for progressive frame resize on PrP channel */
BUG(); /* currently not support */
/* progressvie frame: band mode */
vdoa_write_register(vdoa, VDOAIEBA00, vdoa->iram_paddr);
vdoa_write_register(vdoa, VDOAIEBA10,
vdoa->iram_paddr + band_size);
if (!params->pfs)
vdoa_write_register(vdoa, VDOAIUBO,
params->width * params->band_lines);
dev_dbg(vdoa->dev, "total band_size:0x%x\n", band_size*2);
} else {
/* progressive frame: mem->mem, non-band mode */
vdoa->field = VDOA_FRAME;
vdoa_write_register(vdoa, VDOAVEBA0, params->vframe_buf.veba);
vdoa_write_register(vdoa, VDOAVUBO, params->vframe_buf.vubo);
vdoa_write_register(vdoa, VDOAIEBA00, params->ieba0);
if (!params->pfs)
/* note: iubo is relative value, based on ieba0 */
vdoa_write_register(vdoa, VDOAIUBO,
params->width * params->height);
}
vdoa->state = VDOA_SETUP;
return 0;
}
void vdoa_get_output_buf(vdoa_handle_t handle, struct vdoa_ipu_buf *buf)
{
u32 data;
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
CHECK_NULL_PTR(vdoa);
CHECK_STATE(VDOA_SETUP, return);
vdoa->state = VDOA_GET_OBUF;
memset(buf, 0, sizeof(*buf));
vdoa_read_register(vdoa, VDOAC, &data);
switch (vdoa->field) {
case VDOA_FRAME:
case VDOA_PREV_FIELD:
vdoa_read_register(vdoa, VDOAIEBA00, &buf->ieba0);
if (data & VDOAC_SYNC_BAND_MODE)
vdoa_read_register(vdoa, VDOAIEBA10, &buf->ieba1);
break;
case VDOA_CURR_FIELD:
vdoa_read_register(vdoa, VDOAIEBA01, &buf->ieba0);
vdoa_read_register(vdoa, VDOAIEBA11, &buf->ieba1);
break;
case VDOA_NEXT_FIELD:
vdoa_read_register(vdoa, VDOAIEBA02, &buf->ieba0);
vdoa_read_register(vdoa, VDOAIEBA12, &buf->ieba1);
break;
default:
BUG();
break;
}
if (!(data & VDOAC_PFS_YUYV))
vdoa_read_register(vdoa, VDOAIUBO, &buf->iubo);
}
int vdoa_start(vdoa_handle_t handle, int timeout_ms)
{
int ret;
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
CHECK_NULL_PTR(vdoa);
CHECK_STATE(VDOA_GET_OBUF, return -EINVAL);
vdoa->state = VDOA_START;
init_completion(&vdoa->comp);
vdoa_write_register(vdoa, VDOAIST,
VDOAIEIST_TRANSFER_ERR | VDOAIEIST_TRANSFER_END);
vdoa_write_register(vdoa, VDOAIE,
VDOAIEIST_TRANSFER_ERR | VDOAIEIST_TRANSFER_END);
enable_irq(vdoa->irq);
vdoa_write_register(vdoa, VDOASRR, VDOASRR_START_XFER);
dump_registers(vdoa);
ret = wait_for_completion_timeout(&vdoa->comp,
msecs_to_jiffies(timeout_ms));
return ret > 0 ? 0 : -ETIMEDOUT;
}
void vdoa_stop(vdoa_handle_t handle)
{
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
CHECK_NULL_PTR(vdoa);
CHECK_STATE(VDOA_GET | VDOA_START | VDOA_INIRQ, return);
vdoa->state = VDOA_STOP;
disable_irq(vdoa->irq);
vdoa_write_register(vdoa, VDOASRR, VDOASRR_SWRST);
}
void vdoa_get_handle(vdoa_handle_t *handle)
{
struct vdoa_info *vdoa = g_vdoa;
CHECK_NULL_PTR(handle);
*handle = (vdoa_handle_t *)NULL;
CHECK_STATE(VDOA_INIT, return);
mutex_lock(&vdoa_lock);
clk_prepare_enable(vdoa->vdoa_clk);
vdoa->state = VDOA_GET;
vdoa->field = VDOA_NULL;
vdoa_write_register(vdoa, VDOASRR, VDOASRR_SWRST);
*handle = (vdoa_handle_t *)vdoa;
}
void vdoa_put_handle(vdoa_handle_t *handle)
{
struct vdoa_info *vdoa = (struct vdoa_info *)(*handle);
CHECK_NULL_PTR(vdoa);
CHECK_STATE(VDOA_STOP, return);
if (vdoa != g_vdoa)
BUG();
clk_disable_unprepare(vdoa->vdoa_clk);
vdoa->state = VDOA_PUT;
*handle = (vdoa_handle_t *)NULL;
mutex_unlock(&vdoa_lock);
}
static irqreturn_t vdoa_irq_handler(int irq, void *data)
{
u32 status, mask, val;
struct vdoa_info *vdoa = data;
CHECK_NULL_PTR(vdoa);
CHECK_STATE(VDOA_START, return IRQ_HANDLED);
vdoa->state = VDOA_INIRQ;
vdoa_read_register(vdoa, VDOAIST, &status);
vdoa_read_register(vdoa, VDOAIE, &mask);
val = status & mask;
vdoa_write_register(vdoa, VDOAIST, val);
if (VDOAIEIST_TRANSFER_ERR & val)
dev_err(vdoa->dev, "vdoa Transfer err irq!\n");
if (VDOAIEIST_TRANSFER_END & val)
dev_dbg(vdoa->dev, "vdoa Transfer end irq!\n");
if (0 == val) {
dev_err(vdoa->dev, "vdoa unknown irq!\n");
BUG();
}
complete(&vdoa->comp);
return IRQ_HANDLED;
}
/* IRAM Size in Kbytes, example:vdoa_iram_size=64, 64KBytes */
static int __init vdoa_iram_size_setup(char *options)
{
int ret;
ret = kstrtoul(options, 0, &iram_size);
if (ret)
iram_size = 0;
else
iram_size *= SZ_1K;
return 1;
}
__setup("vdoa_iram_size=", vdoa_iram_size_setup);
static const struct of_device_id imx_vdoa_dt_ids[] = {
{ .compatible = "fsl,imx6q-vdoa", },
{ /* sentinel */ }
};
static int vdoa_probe(struct platform_device *pdev)
{
int ret;
struct vdoa_info *vdoa;
struct resource *res;
struct resource *res_irq;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "can't get device resources\n");
return -ENOENT;
}
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res_irq) {
dev_err(dev, "failed to get irq resource\n");
return -ENOENT;
}
vdoa = devm_kzalloc(dev, sizeof(struct vdoa_info), GFP_KERNEL);
if (!vdoa)
return -ENOMEM;
vdoa->dev = dev;
vdoa->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (!vdoa->reg_base)
return -EBUSY;
vdoa->irq = res_irq->start;
ret = devm_request_irq(dev, vdoa->irq, vdoa_irq_handler, 0,
"vdoa", vdoa);
if (ret) {
dev_err(dev, "can't claim irq %d\n", vdoa->irq);
return ret;
}
disable_irq(vdoa->irq);
vdoa->vdoa_clk = devm_clk_get(dev, NULL);
if (IS_ERR(vdoa->vdoa_clk)) {
dev_err(dev, "failed to get vdoa_clk\n");
return PTR_ERR(vdoa->vdoa_clk);
}
vdoa->iram_pool = of_gen_pool_get(np, "iram", 0);
if (!vdoa->iram_pool) {
dev_err(&pdev->dev, "iram pool not available\n");
return -ENOMEM;
}
if ((iram_size == 0) || (iram_size > MAX_VDOA_IRAM_SIZE))
iram_size = VDOA_IRAM_SIZE;
vdoa->iram_base = gen_pool_alloc(vdoa->iram_pool, iram_size);
if (!vdoa->iram_base) {
dev_err(&pdev->dev, "unable to alloc iram\n");
return -ENOMEM;
}
vdoa->iram_paddr = gen_pool_virt_to_phys(vdoa->iram_pool,
vdoa->iram_base);
dev_dbg(dev, "iram_base:0x%lx,iram_paddr:0x%lx,size:0x%lx\n",
vdoa->iram_base, vdoa->iram_paddr, iram_size);
vdoa->state = VDOA_INIT;
dev_set_drvdata(dev, vdoa);
g_vdoa = vdoa;
dev_info(dev, "i.MX Video Data Order Adapter(VDOA) driver probed\n");
return 0;
}
static int vdoa_remove(struct platform_device *pdev)
{
struct vdoa_info *vdoa = dev_get_drvdata(&pdev->dev);
gen_pool_free(vdoa->iram_pool, vdoa->iram_base, iram_size);
kfree(vdoa);
dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
static struct platform_driver vdoa_driver = {
.driver = {
.name = "mxc_vdoa",
.of_match_table = imx_vdoa_dt_ids,
},
.probe = vdoa_probe,
.remove = vdoa_remove,
};
static int __init vdoa_init(void)
{
int err;
err = platform_driver_register(&vdoa_driver);
if (err) {
pr_err("vdoa_driver register failed\n");
return -ENODEV;
}
return 0;
}
static void __exit vdoa_cleanup(void)
{
platform_driver_unregister(&vdoa_driver);
}
module_init(vdoa_init);
module_exit(vdoa_cleanup);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("i.MX Video Data Order Adapter(VDOA) driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2012-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __VDOA_H__
#define __VDOA_H__
#define VDOA_PFS_YUYV (1)
#define VDOA_PFS_NV12 (0)
struct vfield_buf {
u32 prev_veba;
u32 cur_veba;
u32 next_veba;
u32 vubo;
};
struct vframe_buf {
u32 veba;
u32 vubo;
};
struct vdoa_params {
u32 width;
u32 height;
int vpu_stride;
int interlaced;
int scan_order;
int ipu_num;
int band_lines;
int band_mode;
int pfs;
u32 ieba0;
u32 ieba1;
u32 ieba2;
struct vframe_buf vframe_buf;
struct vfield_buf vfield_buf;
};
struct vdoa_ipu_buf {
u32 ieba0;
u32 ieba1;
u32 iubo;
};
struct vdoa_info;
typedef void *vdoa_handle_t;
int vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params);
void vdoa_get_output_buf(vdoa_handle_t handle, struct vdoa_ipu_buf *buf);
int vdoa_start(vdoa_handle_t handle, int timeout_ms);
void vdoa_stop(vdoa_handle_t handle);
void vdoa_get_handle(vdoa_handle_t *handle);
void vdoa_put_handle(vdoa_handle_t *handle);
#endif

View File

@ -1,29 +1,58 @@
config FB_MXC
tristate "MXC Framebuffer support"
depends on FB
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select FB_MODE_HELPERS
default y
help
This is a framebuffer device for the MXC LCD Controller.
See <http://www.linux-fbdev.org/> for information on framebuffer
devices.
tristate "MXC Framebuffer support"
depends on FB
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select FB_MODE_HELPERS
default y
help
This is a framebuffer device for the MXC LCD Controller.
See <http://www.linux-fbdev.org/> for information on framebuffer
devices.
If you plan to use the LCD display with your MXC system, say
Y here.
If you plan to use the LCD display with your MXC system, say
Y here.
config FB_MXC_SYNC_PANEL
depends on FB_MXC
tristate "Synchronous Panel Framebuffer"
depends on FB_MXC
depends on MXC_IPU_V3
tristate "Synchronous Panel Framebuffer"
config FB_MXC_MIPI_DSI_SAMSUNG
tristate "MXC MIPI_DSI_SAMSUNG"
depends on FB_MXC_SYNC_PANEL
depends on FB_MXS
tristate "MXC MIPI_DSI_SAMSUNG"
depends on FB_MXC_SYNC_PANEL
depends on FB_MXS
config FB_MXC_TRULY_WVGA_SYNC_PANEL
tristate "TRULY WVGA Panel"
depends on FB_MXC_SYNC_PANEL
depends on FB_MXC_MIPI_DSI_SAMSUNG
tristate "TRULY WVGA Panel"
depends on FB_MXC_SYNC_PANEL
depends on FB_MXC_MIPI_DSI_SAMSUNG
config FB_MXC_LDB
tristate "MXC LDB"
depends on FB_MXC_SYNC_PANEL
depends on MXC_IPU_V3 || FB_MXS
select VIDEOMODE_HELPERS
config FB_MXC_HDMI
depends on FB_MXC_SYNC_PANEL
depends on MXC_IPU_V3
depends on I2C
tristate "MXC HDMI driver support"
select MFD_MXC_HDMI
help
Driver for the on-chip MXC HDMI controller.
config FB_MXC_EDID
depends on FB_MXC && I2C
tristate "MXC EDID support"
default y
config HANNSTAR_CABC
tristate "Hannstar CABC function"
help
Say yes here to support switching on/off Hannstar CABC
function. This function turns backlight density of a
display panel automatically according to the content
shown on the panel.

View File

@ -1,3 +1,8 @@
obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o
obj-$(CONFIG_FB_MXC_MIPI_DSI_SAMSUNG) += mipi_dsi_samsung.o
obj-$(CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL) += mxcfb_hx8369_wvga.o
obj-$(CONFIG_FB_MXC_LDB) += ldb.o
obj-$(CONFIG_FB_MXC_HDMI) += mxc_hdmi.o
obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o
obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o mxc_lcdif.o mxc_ipuv3_fb.o
obj-$(CONFIG_FB_MXS_SII902X) += mxsfb_sii902x.o
obj-$(CONFIG_HANNSTAR_CABC) += hannstar_cabc.o

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#define DRIVER_NAME "hannstar-cabc"
static const struct of_device_id cabc_dt_ids[] = {
{ .compatible = "hannstar,cabc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, cabc_dt_ids);
static int cabc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node, *pp;
int cabc_gpio, ret = 0;
bool cabc_enable;
unsigned long gpio_flag;
enum of_gpio_flags flags;
for_each_child_of_node(np, pp) {
if (!of_find_property(pp, "gpios", NULL)) {
dev_warn(&pdev->dev, "Found interface without "
"gpios\n");
continue;
}
cabc_gpio = of_get_gpio_flags(pp, 0, &flags);
if (!gpio_is_valid(cabc_gpio)) {
ret = cabc_gpio;
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Failed to get gpio flags, "
"error: %d\n", ret);
return ret;
}
cabc_enable = of_property_read_bool(pp, "cabc-enable");
if (flags & OF_GPIO_ACTIVE_LOW)
gpio_flag = cabc_enable ?
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
else
gpio_flag = cabc_enable ?
GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
devm_gpio_request_one(&pdev->dev, cabc_gpio,
gpio_flag, "hannstar-cabc");
}
return ret;
}
static struct platform_driver cabc_driver = {
.probe = cabc_probe,
.driver = {
.of_match_table = cabc_dt_ids,
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
module_platform_driver(cabc_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("Hannstar CABC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRIVER_NAME);

View File

@ -0,0 +1,913 @@
/*
* Copyright (C) 2012-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
#include "mxc_dispdrv.h"
#define DRIVER_NAME "ldb"
#define LDB_BGREF_RMODE_INT (0x1 << 15)
#define LDB_DI1_VS_POL_ACT_LOW (0x1 << 10)
#define LDB_DI0_VS_POL_ACT_LOW (0x1 << 9)
#define LDB_BIT_MAP_CH1_JEIDA (0x1 << 8)
#define LDB_BIT_MAP_CH0_JEIDA (0x1 << 6)
#define LDB_DATA_WIDTH_CH1_24 (0x1 << 7)
#define LDB_DATA_WIDTH_CH0_24 (0x1 << 5)
#define LDB_CH1_MODE_MASK (0x3 << 2)
#define LDB_CH1_MODE_EN_TO_DI1 (0x3 << 2)
#define LDB_CH1_MODE_EN_TO_DI0 (0x1 << 2)
#define LDB_CH0_MODE_MASK (0x3 << 0)
#define LDB_CH0_MODE_EN_TO_DI1 (0x3 << 0)
#define LDB_CH0_MODE_EN_TO_DI0 (0x1 << 0)
#define LDB_SPLIT_MODE_EN (0x1 << 4)
#define INVALID_BUS_REG (~0UL)
struct crtc_mux {
enum crtc crtc;
u32 val;
};
struct bus_mux {
int reg;
int shift;
int mask;
int crtc_mux_num;
const struct crtc_mux *crtcs;
};
struct ldb_info {
bool split_cap;
bool dual_cap;
bool ext_bgref_cap;
bool clk_fixup;
int ctrl_reg;
int bus_mux_num;
const struct bus_mux *buses;
};
struct ldb_data;
struct ldb_chan {
struct ldb_data *ldb;
struct fb_info *fbi;
struct videomode vm;
enum crtc crtc;
int chno;
bool is_used;
bool online;
};
struct ldb_data {
struct regmap *regmap;
struct device *dev;
struct mxc_dispdrv_handle *mddh;
struct ldb_chan chan[2];
int bus_mux_num;
const struct bus_mux *buses;
int primary_chno;
int ctrl_reg;
u32 ctrl;
bool spl_mode;
bool dual_mode;
bool clk_fixup;
struct clk *di_clk[4];
struct clk *ldb_di_clk[2];
struct clk *div_3_5_clk[2];
struct clk *div_7_clk[2];
struct clk *div_sel_clk[2];
};
static const struct crtc_mux imx6q_lvds0_crtc_mux[] = {
{
.crtc = CRTC_IPU1_DI0,
.val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU1_DI0,
}, {
.crtc = CRTC_IPU1_DI1,
.val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU1_DI1,
}, {
.crtc = CRTC_IPU2_DI0,
.val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU2_DI0,
}, {
.crtc = CRTC_IPU2_DI1,
.val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU2_DI1,
}
};
static const struct crtc_mux imx6q_lvds1_crtc_mux[] = {
{
.crtc = CRTC_IPU1_DI0,
.val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU1_DI0,
}, {
.crtc = CRTC_IPU1_DI1,
.val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU1_DI1,
}, {
.crtc = CRTC_IPU2_DI0,
.val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU2_DI0,
}, {
.crtc = CRTC_IPU2_DI1,
.val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU2_DI1,
}
};
static const struct bus_mux imx6q_ldb_buses[] = {
{
.reg = IOMUXC_GPR3,
.shift = 6,
.mask = IMX6Q_GPR3_LVDS0_MUX_CTL_MASK,
.crtc_mux_num = ARRAY_SIZE(imx6q_lvds0_crtc_mux),
.crtcs = imx6q_lvds0_crtc_mux,
}, {
.reg = IOMUXC_GPR3,
.shift = 8,
.mask = IMX6Q_GPR3_LVDS1_MUX_CTL_MASK,
.crtc_mux_num = ARRAY_SIZE(imx6q_lvds1_crtc_mux),
.crtcs = imx6q_lvds1_crtc_mux,
}
};
static const struct ldb_info imx6q_ldb_info = {
.split_cap = true,
.dual_cap = true,
.ext_bgref_cap = false,
.clk_fixup = false,
.ctrl_reg = IOMUXC_GPR2,
.bus_mux_num = ARRAY_SIZE(imx6q_ldb_buses),
.buses = imx6q_ldb_buses,
};
static const struct ldb_info imx6qp_ldb_info = {
.split_cap = true,
.dual_cap = true,
.ext_bgref_cap = false,
.clk_fixup = true,
.ctrl_reg = IOMUXC_GPR2,
.bus_mux_num = ARRAY_SIZE(imx6q_ldb_buses),
.buses = imx6q_ldb_buses,
};
static const struct crtc_mux imx6dl_lvds0_crtc_mux[] = {
{
.crtc = CRTC_IPU1_DI0,
.val = IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI0,
}, {
.crtc = CRTC_IPU1_DI1,
.val = IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI1,
}, {
.crtc = CRTC_LCDIF1,
.val = IMX6DL_GPR3_LVDS0_MUX_CTL_LCDIF,
}
};
static const struct crtc_mux imx6dl_lvds1_crtc_mux[] = {
{
.crtc = CRTC_IPU1_DI0,
.val = IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI0,
}, {
.crtc = CRTC_IPU1_DI1,
.val = IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI1,
}, {
.crtc = CRTC_LCDIF1,
.val = IMX6DL_GPR3_LVDS1_MUX_CTL_LCDIF,
}
};
static const struct bus_mux imx6dl_ldb_buses[] = {
{
.reg = IOMUXC_GPR3,
.shift = 6,
.mask = IMX6DL_GPR3_LVDS0_MUX_CTL_MASK,
.crtc_mux_num = ARRAY_SIZE(imx6dl_lvds0_crtc_mux),
.crtcs = imx6dl_lvds0_crtc_mux,
}, {
.reg = IOMUXC_GPR3,
.shift = 8,
.mask = IMX6DL_GPR3_LVDS1_MUX_CTL_MASK,
.crtc_mux_num = ARRAY_SIZE(imx6dl_lvds1_crtc_mux),
.crtcs = imx6dl_lvds1_crtc_mux,
}
};
static const struct ldb_info imx6dl_ldb_info = {
.split_cap = true,
.dual_cap = true,
.ext_bgref_cap = false,
.clk_fixup = false,
.ctrl_reg = IOMUXC_GPR2,
.bus_mux_num = ARRAY_SIZE(imx6dl_ldb_buses),
.buses = imx6dl_ldb_buses,
};
static const struct crtc_mux imx6sx_lvds_crtc_mux[] = {
{
.crtc = CRTC_LCDIF1,
.val = IMX6SX_GPR5_DISP_MUX_LDB_CTRL_LCDIF1,
}, {
.crtc = CRTC_LCDIF2,
.val = IMX6SX_GPR5_DISP_MUX_LDB_CTRL_LCDIF2,
}
};
static const struct bus_mux imx6sx_ldb_buses[] = {
{
.reg = IOMUXC_GPR5,
.shift = 3,
.mask = IMX6SX_GPR5_DISP_MUX_LDB_CTRL_MASK,
.crtc_mux_num = ARRAY_SIZE(imx6sx_lvds_crtc_mux),
.crtcs = imx6sx_lvds_crtc_mux,
}
};
static const struct ldb_info imx6sx_ldb_info = {
.split_cap = false,
.dual_cap = false,
.ext_bgref_cap = false,
.clk_fixup = false,
.ctrl_reg = IOMUXC_GPR6,
.bus_mux_num = ARRAY_SIZE(imx6sx_ldb_buses),
.buses = imx6sx_ldb_buses,
};
static const struct crtc_mux imx53_lvds0_crtc_mux[] = {
{ .crtc = CRTC_IPU1_DI0, },
};
static const struct crtc_mux imx53_lvds1_crtc_mux[] = {
{ .crtc = CRTC_IPU1_DI1, }
};
static const struct bus_mux imx53_ldb_buses[] = {
{
.reg = INVALID_BUS_REG,
.crtc_mux_num = ARRAY_SIZE(imx53_lvds0_crtc_mux),
.crtcs = imx53_lvds0_crtc_mux,
}, {
.reg = INVALID_BUS_REG,
.crtc_mux_num = ARRAY_SIZE(imx53_lvds1_crtc_mux),
.crtcs = imx53_lvds1_crtc_mux,
}
};
static const struct ldb_info imx53_ldb_info = {
.split_cap = true,
.dual_cap = false,
.ext_bgref_cap = true,
.clk_fixup = false,
.ctrl_reg = IOMUXC_GPR2,
.bus_mux_num = ARRAY_SIZE(imx53_ldb_buses),
.buses = imx53_ldb_buses,
};
static const struct of_device_id ldb_dt_ids[] = {
{ .compatible = "fsl,imx6qp-ldb", .data = &imx6qp_ldb_info, },
{ .compatible = "fsl,imx6q-ldb", .data = &imx6q_ldb_info, },
{ .compatible = "fsl,imx6dl-ldb", .data = &imx6dl_ldb_info, },
{ .compatible = "fsl,imx6sx-ldb", .data = &imx6sx_ldb_info, },
{ .compatible = "fsl,imx53-ldb", .data = &imx53_ldb_info, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ldb_dt_ids);
static int ldb_init(struct mxc_dispdrv_handle *mddh,
struct mxc_dispdrv_setting *setting)
{
struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
struct device *dev = ldb->dev;
struct fb_info *fbi = setting->fbi;
struct ldb_chan *chan;
struct fb_videomode fb_vm;
int chno;
chno = ldb->chan[ldb->primary_chno].is_used ?
!ldb->primary_chno : ldb->primary_chno;
chan = &ldb->chan[chno];
if (chan->is_used) {
dev_err(dev, "LVDS channel%d is already used\n", chno);
return -EBUSY;
}
if (!chan->online) {
dev_err(dev, "LVDS channel%d is not online\n", chno);
return -ENODEV;
}
chan->is_used = true;
chan->fbi = fbi;
fb_videomode_from_videomode(&chan->vm, &fb_vm);
INIT_LIST_HEAD(&fbi->modelist);
fb_add_videomode(&fb_vm, &fbi->modelist);
fb_videomode_to_var(&fbi->var, &fb_vm);
setting->crtc = chan->crtc;
return 0;
}
static int get_di_clk_id(struct ldb_chan chan, int *id)
{
struct ldb_data *ldb = chan.ldb;
int i = 0, chno = chan.chno, mask, shift;
enum crtc crtc;
u32 val;
/* no pre-muxing, such as mx53 */
if (ldb->buses[chno].reg == INVALID_BUS_REG) {
*id = chno;
return 0;
}
for (; i < ldb->buses[chno].crtc_mux_num; i++) {
crtc = ldb->buses[chno].crtcs[i].crtc;
val = ldb->buses[chno].crtcs[i].val;
mask = ldb->buses[chno].mask;
shift = ldb->buses[chno].shift;
if (chan.crtc == crtc) {
*id = (val & mask) >> shift;
return 0;
}
}
return -EINVAL;
}
static int get_mux_val(struct bus_mux bus_mux, enum crtc crtc,
u32 *mux_val)
{
int i = 0;
for (; i < bus_mux.crtc_mux_num; i++)
if (bus_mux.crtcs[i].crtc == crtc) {
*mux_val = bus_mux.crtcs[i].val;
return 0;
}
return -EINVAL;
}
static int find_ldb_chno(struct ldb_data *ldb,
struct fb_info *fbi, int *chno)
{
struct device *dev = ldb->dev;
int i = 0;
for (; i < 2; i++)
if (ldb->chan[i].fbi == fbi) {
*chno = ldb->chan[i].chno;
return 0;
}
dev_err(dev, "failed to find channel number\n");
return -EINVAL;
}
static void ldb_disable(struct mxc_dispdrv_handle *mddh,
struct fb_info *fbi);
static int ldb_setup(struct mxc_dispdrv_handle *mddh,
struct fb_info *fbi)
{
struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
struct ldb_chan chan;
struct device *dev = ldb->dev;
struct clk *ldb_di_parent, *ldb_di_sel, *ldb_di_sel_parent;
struct clk *other_ldb_di_sel = NULL;
struct bus_mux bus_mux;
int ret = 0, id = 0, chno, other_chno;
unsigned long serial_clk;
u32 mux_val;
ret = find_ldb_chno(ldb, fbi, &chno);
if (ret < 0)
return ret;
other_chno = chno ? 0 : 1;
chan = ldb->chan[chno];
bus_mux = ldb->buses[chno];
ret = get_di_clk_id(chan, &id);
if (ret < 0) {
dev_err(dev, "failed to get ch%d di clk id\n",
chan.chno);
return ret;
}
ret = get_mux_val(bus_mux, chan.crtc, &mux_val);
if (ret < 0) {
dev_err(dev, "failed to get ch%d mux val\n",
chan.chno);
return ret;
}
if (ldb->clk_fixup) {
/*
* ldb_di_sel_parent(plls) -> ldb_di_sel -> ldb_di[chno] ->
*
* -> div_3_5[chno] ->
* -> | |-> div_sel[chno] -> di[id]
* -> div_7[chno] ->
*/
clk_set_parent(ldb->di_clk[id], ldb->div_sel_clk[chno]);
} else {
/*
* ldb_di_sel_parent(plls) -> ldb_di_sel ->
*
* -> div_3_5[chno] ->
* -> | |-> div_sel[chno] ->
* -> div_7[chno] ->
*
* -> ldb_di[chno] -> di[id]
*/
clk_set_parent(ldb->di_clk[id], ldb->ldb_di_clk[chno]);
}
ldb_di_parent = ldb->spl_mode ? ldb->div_3_5_clk[chno] :
ldb->div_7_clk[chno];
clk_set_parent(ldb->div_sel_clk[chno], ldb_di_parent);
ldb_di_sel = clk_get_parent(ldb_di_parent);
ldb_di_sel_parent = clk_get_parent(ldb_di_sel);
serial_clk = ldb->spl_mode ? chan.vm.pixelclock * 7 / 2 :
chan.vm.pixelclock * 7;
clk_set_rate(ldb_di_sel_parent, serial_clk);
/*
* split mode or dual mode:
* clock tree for the other channel
*/
if (ldb->spl_mode) {
clk_set_parent(ldb->div_sel_clk[other_chno],
ldb->div_3_5_clk[other_chno]);
other_ldb_di_sel =
clk_get_parent(ldb->div_3_5_clk[other_chno]);;
}
if (ldb->dual_mode) {
clk_set_parent(ldb->div_sel_clk[other_chno],
ldb->div_7_clk[other_chno]);
other_ldb_di_sel =
clk_get_parent(ldb->div_7_clk[other_chno]);;
}
if (ldb->spl_mode || ldb->dual_mode)
clk_set_parent(other_ldb_di_sel, ldb_di_sel_parent);
if (!(chan.fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)) {
if (ldb->spl_mode && bus_mux.reg == INVALID_BUS_REG)
/* no pre-muxing, such as mx53 */
ldb->ctrl |= (id == 0 ? LDB_DI0_VS_POL_ACT_LOW :
LDB_DI1_VS_POL_ACT_LOW);
else
ldb->ctrl |= (chno == 0 ? LDB_DI0_VS_POL_ACT_LOW :
LDB_DI1_VS_POL_ACT_LOW);
}
if (bus_mux.reg != INVALID_BUS_REG)
regmap_update_bits(ldb->regmap, bus_mux.reg,
bus_mux.mask, mux_val);
regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
/* disable channel for correct sequence */
ldb_disable(mddh, fbi);
return ret;
}
static int ldb_enable(struct mxc_dispdrv_handle *mddh,
struct fb_info *fbi)
{
struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
struct ldb_chan chan;
struct device *dev = ldb->dev;
struct bus_mux bus_mux;
int ret = 0, id = 0, chno, other_chno;
ret = find_ldb_chno(ldb, fbi, &chno);
if (ret < 0)
return ret;
chan = ldb->chan[chno];
bus_mux = ldb->buses[chno];
if (ldb->spl_mode || ldb->dual_mode) {
other_chno = chno ? 0 : 1;
clk_prepare_enable(ldb->ldb_di_clk[other_chno]);
}
if ((ldb->spl_mode || ldb->dual_mode) &&
bus_mux.reg == INVALID_BUS_REG) {
/* no pre-muxing, such as mx53 */
ret = get_di_clk_id(chan, &id);
if (ret < 0) {
dev_err(dev, "failed to get ch%d di clk id\n",
chan.chno);
return ret;
}
ldb->ctrl |= id ?
(LDB_CH0_MODE_EN_TO_DI1 | LDB_CH1_MODE_EN_TO_DI1) :
(LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI0);
} else {
if (ldb->spl_mode || ldb->dual_mode)
ldb->ctrl |= LDB_CH0_MODE_EN_TO_DI0 |
LDB_CH1_MODE_EN_TO_DI0;
else
ldb->ctrl |= chno ? LDB_CH1_MODE_EN_TO_DI1 :
LDB_CH0_MODE_EN_TO_DI0;
}
regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
return 0;
}
static void ldb_disable(struct mxc_dispdrv_handle *mddh,
struct fb_info *fbi)
{
struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
int ret, chno, other_chno;
ret = find_ldb_chno(ldb, fbi, &chno);
if (ret < 0)
return;
if (ldb->spl_mode || ldb->dual_mode) {
ldb->ctrl &= ~(LDB_CH1_MODE_MASK | LDB_CH0_MODE_MASK);
other_chno = chno ? 0 : 1;
clk_disable_unprepare(ldb->ldb_di_clk[other_chno]);
} else {
ldb->ctrl &= ~(chno ? LDB_CH1_MODE_MASK :
LDB_CH0_MODE_MASK);
}
regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
return;
}
static struct mxc_dispdrv_driver ldb_drv = {
.name = DRIVER_NAME,
.init = ldb_init,
.setup = ldb_setup,
.enable = ldb_enable,
.disable = ldb_disable
};
enum {
LVDS_BIT_MAP_SPWG,
LVDS_BIT_MAP_JEIDA,
};
static const char *ldb_bit_mappings[] = {
[LVDS_BIT_MAP_SPWG] = "spwg",
[LVDS_BIT_MAP_JEIDA] = "jeida",
};
static int of_get_data_mapping(struct device_node *np)
{
const char *bm;
int ret, i;
ret = of_property_read_string(np, "fsl,data-mapping", &bm);
if (ret < 0)
return ret;
for (i = 0; i < ARRAY_SIZE(ldb_bit_mappings); i++)
if (!strcasecmp(bm, ldb_bit_mappings[i]))
return i;
return -EINVAL;
}
static const char *ldb_crtc_mappings[] = {
[CRTC_IPU_DI0] = "ipu-di0",
[CRTC_IPU_DI1] = "ipu-di1",
[CRTC_IPU1_DI0] = "ipu1-di0",
[CRTC_IPU1_DI1] = "ipu1-di1",
[CRTC_IPU2_DI0] = "ipu2-di0",
[CRTC_IPU2_DI1] = "ipu2-di1",
[CRTC_LCDIF] = "lcdif",
[CRTC_LCDIF1] = "lcdif1",
[CRTC_LCDIF2] = "lcdif2",
};
static enum crtc of_get_crtc_mapping(struct device_node *np)
{
const char *cm;
enum crtc i;
int ret;
ret = of_property_read_string(np, "crtc", &cm);
if (ret < 0)
return ret;
for (i = 0; i < ARRAY_SIZE(ldb_crtc_mappings); i++)
if (!strcasecmp(cm, ldb_crtc_mappings[i])) {
switch (i) {
case CRTC_IPU_DI0:
i = CRTC_IPU1_DI0;
break;
case CRTC_IPU_DI1:
i = CRTC_IPU1_DI1;
break;
case CRTC_LCDIF:
i = CRTC_LCDIF1;
break;
default:
break;
}
return i;
}
return -EINVAL;
}
static int mux_count(struct ldb_data *ldb)
{
int i, j, count = 0;
bool should_count[CRTC_MAX];
enum crtc crtc;
for (i = 0; i < CRTC_MAX; i++)
should_count[i] = true;
for (i = 0; i < ldb->bus_mux_num; i++) {
for (j = 0; j < ldb->buses[i].crtc_mux_num; j++) {
crtc = ldb->buses[i].crtcs[j].crtc;
if (should_count[crtc]) {
count++;
should_count[crtc] = false;
}
}
}
return count;
}
static bool is_valid_crtc(struct ldb_data *ldb, enum crtc crtc,
int chno)
{
int i = 0;
if (chno > ldb->bus_mux_num - 1)
return false;
for (; i < ldb->buses[chno].crtc_mux_num; i++)
if (ldb->buses[chno].crtcs[i].crtc == crtc)
return true;
return false;
}
static int ldb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *of_id =
of_match_device(ldb_dt_ids, dev);
const struct ldb_info *ldb_info =
(const struct ldb_info *)of_id->data;
struct device_node *np = dev->of_node, *child;
struct ldb_data *ldb;
bool ext_ref;
int i, data_width, mapping, child_count = 0;
char clkname[16];
ldb = devm_kzalloc(dev, sizeof(*ldb), GFP_KERNEL);
if (!ldb)
return -ENOMEM;
ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
if (IS_ERR(ldb->regmap)) {
dev_err(dev, "failed to get parent regmap\n");
return PTR_ERR(ldb->regmap);
}
ldb->dev = dev;
ldb->bus_mux_num = ldb_info->bus_mux_num;
ldb->buses = ldb_info->buses;
ldb->ctrl_reg = ldb_info->ctrl_reg;
ldb->clk_fixup = ldb_info->clk_fixup;
ldb->primary_chno = -1;
ext_ref = of_property_read_bool(np, "ext-ref");
if (!ext_ref && ldb_info->ext_bgref_cap)
ldb->ctrl |= LDB_BGREF_RMODE_INT;
ldb->spl_mode = of_property_read_bool(np, "split-mode");
if (ldb->spl_mode) {
if (ldb_info->split_cap) {
ldb->ctrl |= LDB_SPLIT_MODE_EN;
dev_info(dev, "split mode\n");
} else {
dev_err(dev, "cannot support split mode\n");
return -EINVAL;
}
}
ldb->dual_mode = of_property_read_bool(np, "dual-mode");
if (ldb->dual_mode) {
if (ldb_info->dual_cap) {
dev_info(dev, "dual mode\n");
} else {
dev_err(dev, "cannot support dual mode\n");
return -EINVAL;
}
}
if (ldb->dual_mode && ldb->spl_mode) {
dev_err(dev, "cannot support dual mode and split mode "
"simultaneously\n");
return -EINVAL;
}
for (i = 0; i < mux_count(ldb); i++) {
sprintf(clkname, "di%d_sel", i);
ldb->di_clk[i] = devm_clk_get(dev, clkname);
if (IS_ERR(ldb->di_clk[i])) {
dev_err(dev, "failed to get clk %s\n", clkname);
return PTR_ERR(ldb->di_clk[i]);
}
}
for_each_child_of_node(np, child) {
struct ldb_chan *chan;
enum crtc crtc;
bool is_primary;
int ret;
ret = of_property_read_u32(child, "reg", &i);
if (ret || i < 0 || i > 1 || i >= ldb->bus_mux_num) {
dev_err(dev, "wrong LVDS channel number\n");
return -EINVAL;
}
if ((ldb->spl_mode || ldb->dual_mode) && i > 0) {
dev_warn(dev, "split mode or dual mode, ignoring "
"second output\n");
continue;
}
if (!of_device_is_available(child))
continue;
if (++child_count > ldb->bus_mux_num) {
dev_err(dev, "too many LVDS channels\n");
return -EINVAL;
}
chan = &ldb->chan[i];
chan->chno = i;
chan->ldb = ldb;
chan->online = true;
is_primary = of_property_read_bool(child, "primary");
if (ldb->bus_mux_num == 1 || (ldb->primary_chno == -1 &&
(is_primary || ldb->spl_mode || ldb->dual_mode)))
ldb->primary_chno = chan->chno;
ret = of_property_read_u32(child, "fsl,data-width",
&data_width);
if (ret || (data_width != 18 && data_width != 24)) {
dev_err(dev, "data width not specified or invalid\n");
return -EINVAL;
}
mapping = of_get_data_mapping(child);
switch (mapping) {
case LVDS_BIT_MAP_SPWG:
if (data_width == 24) {
if (i == 0 || ldb->spl_mode || ldb->dual_mode)
ldb->ctrl |= LDB_DATA_WIDTH_CH0_24;
if (i == 1 || ldb->spl_mode || ldb->dual_mode)
ldb->ctrl |= LDB_DATA_WIDTH_CH1_24;
}
break;
case LVDS_BIT_MAP_JEIDA:
if (data_width == 18) {
dev_err(dev, "JEIDA only support 24bit\n");
return -EINVAL;
}
if (i == 0 || ldb->spl_mode || ldb->dual_mode)
ldb->ctrl |= LDB_DATA_WIDTH_CH0_24 |
LDB_BIT_MAP_CH0_JEIDA;
if (i == 1 || ldb->spl_mode || ldb->dual_mode)
ldb->ctrl |= LDB_DATA_WIDTH_CH1_24 |
LDB_BIT_MAP_CH1_JEIDA;
break;
default:
dev_err(dev, "data mapping not specified or invalid\n");
return -EINVAL;
}
crtc = of_get_crtc_mapping(child);
if (is_valid_crtc(ldb, crtc, chan->chno)) {
ldb->chan[i].crtc = crtc;
} else {
dev_err(dev, "crtc not specified or invalid\n");
return -EINVAL;
}
ret = of_get_videomode(child, &chan->vm, 0);
if (ret)
return -EINVAL;
sprintf(clkname, "ldb_di%d", i);
ldb->ldb_di_clk[i] = devm_clk_get(dev, clkname);
if (IS_ERR(ldb->ldb_di_clk[i])) {
dev_err(dev, "failed to get clk %s\n", clkname);
return PTR_ERR(ldb->ldb_di_clk[i]);
}
sprintf(clkname, "ldb_di%d_div_3_5", i);
ldb->div_3_5_clk[i] = devm_clk_get(dev, clkname);
if (IS_ERR(ldb->div_3_5_clk[i])) {
dev_err(dev, "failed to get clk %s\n", clkname);
return PTR_ERR(ldb->div_3_5_clk[i]);
}
sprintf(clkname, "ldb_di%d_div_7", i);
ldb->div_7_clk[i] = devm_clk_get(dev, clkname);
if (IS_ERR(ldb->div_7_clk[i])) {
dev_err(dev, "failed to get clk %s\n", clkname);
return PTR_ERR(ldb->div_7_clk[i]);
}
sprintf(clkname, "ldb_di%d_div_sel", i);
ldb->div_sel_clk[i] = devm_clk_get(dev, clkname);
if (IS_ERR(ldb->div_sel_clk[i])) {
dev_err(dev, "failed to get clk %s\n", clkname);
return PTR_ERR(ldb->div_sel_clk[i]);
}
}
if (child_count == 0) {
dev_err(dev, "failed to find valid LVDS channel\n");
return -EINVAL;
}
if (ldb->primary_chno == -1) {
dev_err(dev, "failed to know primary channel\n");
return -EINVAL;
}
ldb->mddh = mxc_dispdrv_register(&ldb_drv);
mxc_dispdrv_setdata(ldb->mddh, ldb);
dev_set_drvdata(&pdev->dev, ldb);
return 0;
}
static int ldb_remove(struct platform_device *pdev)
{
struct ldb_data *ldb = dev_get_drvdata(&pdev->dev);
mxc_dispdrv_puthandle(ldb->mddh);
mxc_dispdrv_unregister(ldb->mddh);
return 0;
}
static struct platform_driver ldb_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = ldb_dt_ids,
},
.probe = ldb_probe,
.remove = ldb_remove,
};
module_platform_driver(ldb_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("LDB driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRIVER_NAME);

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*

View File

@ -0,0 +1,769 @@
/*
* Copyright 2009-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
*/
/*!
* @file mxc_edid.c
*
* @brief MXC EDID driver
*
* @ingroup Framebuffer
*/
/*!
* Include files
*/
#include <linux/i2c.h>
#include <linux/fb.h>
#include <video/mxc_edid.h>
#include "../edid.h"
#undef DEBUG /* define this for verbose EDID parsing output */
#ifdef DEBUG
#define DPRINTK(fmt, args...) printk(fmt, ## args)
#else
#define DPRINTK(fmt, args...)
#endif
const struct fb_videomode mxc_cea_mode[64] = {
/* #1: 640x480p@59.94/60Hz 4:3 */
[1] = {
NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
/* #2: 720x480p@59.94/60Hz 4:3 */
[2] = {
NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
/* #3: 720x480p@59.94/60Hz 16:9 */
[3] = {
NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #4: 1280x720p@59.94/60Hz 16:9 */
[4] = {
NULL, 60, 1280, 720, 13468, 220, 110, 20, 5, 40, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
},
/* #5: 1920x1080i@59.94/60Hz 16:9 */
[5] = {
NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #6: 720(1440)x480iH@59.94/60Hz 4:3 */
[6] = {
NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
FB_VMODE_INTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
/* #7: 720(1440)x480iH@59.94/60Hz 16:9 */
[7] = {
NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #8: 720(1440)x240pH@59.94/60Hz 4:3 */
[8] = {
NULL, 60, 1440, 240, 37108, 114, 38, 15, 4, 124, 3, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
/* #9: 720(1440)x240pH@59.94/60Hz 16:9 */
[9] = {
NULL, 60, 1440, 240, 37108, 114, 38, 15, 4, 124, 3, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #14: 1440x480p@59.94/60Hz 4:3 */
[14] = {
NULL, 60, 1440, 480, 18500, 120, 32, 30, 9, 124, 6, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
/* #15: 1440x480p@59.94/60Hz 16:9 */
[15] = {
NULL, 60, 1440, 480, 18500, 120, 32, 30, 9, 124, 6, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #16: 1920x1080p@60Hz 16:9 */
[16] = {
NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #17: 720x576pH@50Hz 4:3 */
[17] = {
NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
/* #18: 720x576pH@50Hz 16:9 */
[18] = {
NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #19: 1280x720p@50Hz */
[19] = {
NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #20: 1920x1080i@50Hz */
[20] = {
NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #23: 720(1440)x288pH@50Hz 4:3 */
[23] = {
NULL, 50, 1440, 288, 37037, 138, 24, 19, 2, 126, 3, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
/* #24: 720(1440)x288pH@50Hz 16:9 */
[24] = {
NULL, 50, 1440, 288, 37037, 138, 24, 19, 2, 126, 3, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #29: 720(1440)x576pH@50Hz 4:3 */
[29] = {
NULL, 50, 1440, 576, 18518, 136, 24, 39, 5, 128, 5, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
/* #30: 720(1440)x576pH@50Hz 16:9 */
[30] = {
NULL, 50, 1440, 576, 18518, 136, 24, 39, 5, 128, 5, 0,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #31: 1920x1080p@50Hz */
[31] = {
NULL, 50, 1920, 1080, 6734, 148, 528, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #32: 1920x1080p@23.98/24Hz */
[32] = {
NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #33: 1920x1080p@25Hz */
[33] = {
NULL, 25, 1920, 1080, 13468, 148, 528, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #34: 1920x1080p@30Hz */
[34] = {
NULL, 30, 1920, 1080, 13468, 148, 88, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #41: 1280x720p@100Hz 16:9 */
[41] = {
NULL, 100, 1280, 720, 6734, 220, 440, 20, 5, 40, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
},
/* #47: 1280x720p@119.88/120Hz 16:9 */
[47] = {
NULL, 120, 1280, 720, 6734, 220, 110, 20, 5, 40, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
},
};
/*
* We have a special version of fb_mode_is_equal that ignores
* pixclock, since for many CEA modes, 2 frequencies are supported
* e.g. 640x480 @ 60Hz or 59.94Hz
*/
int mxc_edid_fb_mode_is_equal(bool use_aspect,
const struct fb_videomode *mode1,
const struct fb_videomode *mode2)
{
u32 mask;
if (use_aspect)
mask = ~0;
else
mask = ~FB_VMODE_ASPECT_MASK;
return (mode1->xres == mode2->xres &&
mode1->yres == mode2->yres &&
mode1->hsync_len == mode2->hsync_len &&
mode1->vsync_len == mode2->vsync_len &&
mode1->left_margin == mode2->left_margin &&
mode1->right_margin == mode2->right_margin &&
mode1->upper_margin == mode2->upper_margin &&
mode1->lower_margin == mode2->lower_margin &&
mode1->sync == mode2->sync &&
/* refresh check, 59.94Hz and 60Hz have the same parameter
* in struct of mxc_cea_mode */
abs(mode1->refresh - mode2->refresh) <= 1 &&
(mode1->vmode & mask) == (mode2->vmode & mask));
}
static void get_detailed_timing(unsigned char *block,
struct fb_videomode *mode)
{
mode->xres = H_ACTIVE;
mode->yres = V_ACTIVE;
mode->pixclock = PIXEL_CLOCK;
mode->pixclock /= 1000;
mode->pixclock = KHZ2PICOS(mode->pixclock);
mode->right_margin = H_SYNC_OFFSET;
mode->left_margin = (H_ACTIVE + H_BLANKING) -
(H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
V_SYNC_WIDTH;
mode->lower_margin = V_SYNC_OFFSET;
mode->hsync_len = H_SYNC_WIDTH;
mode->vsync_len = V_SYNC_WIDTH;
if (HSYNC_POSITIVE)
mode->sync |= FB_SYNC_HOR_HIGH_ACT;
if (VSYNC_POSITIVE)
mode->sync |= FB_SYNC_VERT_HIGH_ACT;
mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
(V_ACTIVE + V_BLANKING));
if (INTERLACED) {
mode->yres *= 2;
mode->upper_margin *= 2;
mode->lower_margin *= 2;
mode->vsync_len *= 2;
mode->vmode |= FB_VMODE_INTERLACED;
}
mode->flag = FB_MODE_IS_DETAILED;
if ((H_SIZE / 16) == (V_SIZE / 9))
mode->vmode |= FB_VMODE_ASPECT_16_9;
else if ((H_SIZE / 4) == (V_SIZE / 3))
mode->vmode |= FB_VMODE_ASPECT_4_3;
else if ((mode->xres / 16) == (mode->yres / 9))
mode->vmode |= FB_VMODE_ASPECT_16_9;
else if ((mode->xres / 4) == (mode->yres / 3))
mode->vmode |= FB_VMODE_ASPECT_4_3;
if (mode->vmode & FB_VMODE_ASPECT_16_9)
DPRINTK("Aspect ratio: 16:9\n");
if (mode->vmode & FB_VMODE_ASPECT_4_3)
DPRINTK("Aspect ratio: 4:3\n");
DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000);
DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
(VSYNC_POSITIVE) ? "+" : "-");
}
int mxc_edid_parse_ext_blk(unsigned char *edid,
struct mxc_edid_cfg *cfg,
struct fb_monspecs *specs)
{
char detail_timing_desc_offset;
struct fb_videomode *mode, *m;
unsigned char index = 0x0;
unsigned char *block;
int i, num = 0, revision;
if (edid[index++] != 0x2) /* only support cea ext block now */
return 0;
revision = edid[index++];
DPRINTK("cea extent revision %d\n", revision);
mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
if (mode == NULL)
return -1;
detail_timing_desc_offset = edid[index++];
if (revision >= 2) {
cfg->cea_underscan = (edid[index] >> 7) & 0x1;
cfg->cea_basicaudio = (edid[index] >> 6) & 0x1;
cfg->cea_ycbcr444 = (edid[index] >> 5) & 0x1;
cfg->cea_ycbcr422 = (edid[index] >> 4) & 0x1;
DPRINTK("CEA underscan %d\n", cfg->cea_underscan);
DPRINTK("CEA basicaudio %d\n", cfg->cea_basicaudio);
DPRINTK("CEA ycbcr444 %d\n", cfg->cea_ycbcr444);
DPRINTK("CEA ycbcr422 %d\n", cfg->cea_ycbcr422);
}
if (revision >= 3) {
/* short desc */
DPRINTK("CEA Short desc timmings\n");
index++;
while (index < detail_timing_desc_offset) {
unsigned char tagcode, blklen;
tagcode = (edid[index] >> 5) & 0x7;
blklen = (edid[index]) & 0x1f;
DPRINTK("Tagcode %x Len %d\n", tagcode, blklen);
switch (tagcode) {
case 0x2: /*Video data block*/
{
int cea_idx;
i = 0;
while (i < blklen) {
index++;
cea_idx = edid[index] & 0x7f;
if (cea_idx < ARRAY_SIZE(mxc_cea_mode) &&
(mxc_cea_mode[cea_idx].xres)) {
DPRINTK("Support CEA Format #%d\n", cea_idx);
mode[num] = mxc_cea_mode[cea_idx];
mode[num].flag |= FB_MODE_IS_STANDARD;
num++;
}
i++;
}
break;
}
case 0x3: /*Vendor specific data*/
{
unsigned char IEEE_reg_iden[3];
unsigned char deep_color;
unsigned char latency_present;
unsigned char I_latency_present;
unsigned char hdmi_video_present;
unsigned char hdmi_3d_present;
unsigned char hdmi_3d_multi_present;
unsigned char hdmi_vic_len;
unsigned char hdmi_3d_len;
unsigned char index_inc = 0;
unsigned char vsd_end;
vsd_end = index + blklen;
IEEE_reg_iden[0] = edid[index+1];
IEEE_reg_iden[1] = edid[index+2];
IEEE_reg_iden[2] = edid[index+3];
cfg->physical_address[0] = (edid[index+4] & 0xf0) >> 4;
cfg->physical_address[1] = (edid[index+4] & 0x0f);
cfg->physical_address[2] = (edid[index+5] & 0xf0) >> 4;
cfg->physical_address[3] = (edid[index+5] & 0x0f);
if ((IEEE_reg_iden[0] == 0x03) &&
(IEEE_reg_iden[1] == 0x0c) &&
(IEEE_reg_iden[2] == 0x00))
cfg->hdmi_cap = 1;
if (blklen > 5) {
deep_color = edid[index+6];
if (deep_color & 0x80)
cfg->vsd_support_ai = true;
if (deep_color & 0x40)
cfg->vsd_dc_48bit = true;
if (deep_color & 0x20)
cfg->vsd_dc_36bit = true;
if (deep_color & 0x10)
cfg->vsd_dc_30bit = true;
if (deep_color & 0x08)
cfg->vsd_dc_y444 = true;
if (deep_color & 0x01)
cfg->vsd_dvi_dual = true;
}
DPRINTK("VSD hdmi capability %d\n", cfg->hdmi_cap);
DPRINTK("VSD support ai %d\n", cfg->vsd_support_ai);
DPRINTK("VSD support deep color 48bit %d\n", cfg->vsd_dc_48bit);
DPRINTK("VSD support deep color 36bit %d\n", cfg->vsd_dc_36bit);
DPRINTK("VSD support deep color 30bit %d\n", cfg->vsd_dc_30bit);
DPRINTK("VSD support deep color y444 %d\n", cfg->vsd_dc_y444);
DPRINTK("VSD support dvi dual %d\n", cfg->vsd_dvi_dual);
if (blklen > 6)
cfg->vsd_max_tmdsclk_rate = edid[index+7] * 5;
DPRINTK("VSD MAX TMDS CLOCK RATE %d\n", cfg->vsd_max_tmdsclk_rate);
if (blklen > 7) {
latency_present = edid[index+8] >> 7;
I_latency_present = (edid[index+8] & 0x40) >> 6;
hdmi_video_present = (edid[index+8] & 0x20) >> 5;
cfg->vsd_cnc3 = (edid[index+8] & 0x8) >> 3;
cfg->vsd_cnc2 = (edid[index+8] & 0x4) >> 2;
cfg->vsd_cnc1 = (edid[index+8] & 0x2) >> 1;
cfg->vsd_cnc0 = edid[index+8] & 0x1;
DPRINTK("VSD cnc0 %d\n", cfg->vsd_cnc0);
DPRINTK("VSD cnc1 %d\n", cfg->vsd_cnc1);
DPRINTK("VSD cnc2 %d\n", cfg->vsd_cnc2);
DPRINTK("VSD cnc3 %d\n", cfg->vsd_cnc3);
DPRINTK("latency_present %d\n", latency_present);
DPRINTK("I_latency_present %d\n", I_latency_present);
DPRINTK("hdmi_video_present %d\n", hdmi_video_present);
} else {
index += blklen;
break;
}
index += 9;
/*latency present */
if (latency_present) {
cfg->vsd_video_latency = edid[index++];
cfg->vsd_audio_latency = edid[index++];
if (I_latency_present) {
cfg->vsd_I_video_latency = edid[index++];
cfg->vsd_I_audio_latency = edid[index++];
} else {
cfg->vsd_I_video_latency = cfg->vsd_video_latency;
cfg->vsd_I_audio_latency = cfg->vsd_audio_latency;
}
DPRINTK("VSD latency video_latency %d\n", cfg->vsd_video_latency);
DPRINTK("VSD latency audio_latency %d\n", cfg->vsd_audio_latency);
DPRINTK("VSD latency I_video_latency %d\n", cfg->vsd_I_video_latency);
DPRINTK("VSD latency I_audio_latency %d\n", cfg->vsd_I_audio_latency);
}
if (hdmi_video_present) {
hdmi_3d_present = edid[index] >> 7;
hdmi_3d_multi_present = (edid[index] & 0x60) >> 5;
index++;
hdmi_vic_len = (edid[index] & 0xe0) >> 5;
hdmi_3d_len = edid[index] & 0x1f;
index++;
DPRINTK("hdmi_3d_present %d\n", hdmi_3d_present);
DPRINTK("hdmi_3d_multi_present %d\n", hdmi_3d_multi_present);
DPRINTK("hdmi_vic_len %d\n", hdmi_vic_len);
DPRINTK("hdmi_3d_len %d\n", hdmi_3d_len);
if (hdmi_vic_len > 0) {
for (i = 0; i < hdmi_vic_len; i++) {
cfg->hdmi_vic[i] = edid[index++];
DPRINTK("HDMI_vic=%d\n", cfg->hdmi_vic[i]);
}
}
if (hdmi_3d_len > 0) {
if (hdmi_3d_present) {
if (hdmi_3d_multi_present == 0x1) {
cfg->hdmi_3d_struct_all = (edid[index] << 8) | edid[index+1];
index_inc = 2;
} else if (hdmi_3d_multi_present == 0x2) {
cfg->hdmi_3d_struct_all = (edid[index] << 8) | edid[index+1];
cfg->hdmi_3d_mask_all = (edid[index+2] << 8) | edid[index+3];
index_inc = 4;
} else
index_inc = 0;
}
DPRINTK("HDMI 3d struct all =0x%x\n", cfg->hdmi_3d_struct_all);
DPRINTK("HDMI 3d mask all =0x%x\n", cfg->hdmi_3d_mask_all);
/* Read 2D vic 3D_struct */
if ((hdmi_3d_len - index_inc) > 0) {
DPRINTK("Support 3D video format\n");
i = 0;
while ((hdmi_3d_len - index_inc) > 0) {
cfg->hdmi_3d_format[i].vic_order_2d = edid[index+index_inc] >> 4;
cfg->hdmi_3d_format[i].struct_3d = edid[index+index_inc] & 0x0f;
index_inc++;
if (cfg->hdmi_3d_format[i].struct_3d == 8) {
cfg->hdmi_3d_format[i].detail_3d = edid[index+index_inc] >> 4;
index_inc++;
} else if (cfg->hdmi_3d_format[i].struct_3d > 8) {
cfg->hdmi_3d_format[i].detail_3d = 0;
index_inc++;
}
DPRINTK("vic_order_2d=%d, 3d_struct=%d, 3d_detail=0x%x\n",
cfg->hdmi_3d_format[i].vic_order_2d,
cfg->hdmi_3d_format[i].struct_3d,
cfg->hdmi_3d_format[i].detail_3d);
i++;
}
}
index += index_inc;
}
}
index = vsd_end;
break;
}
case 0x1: /*Audio data block*/
{
u8 audio_format, max_ch, byte1, byte2, byte3;
i = 0;
cfg->max_channels = 0;
cfg->sample_rates = 0;
cfg->sample_sizes = 0;
while (i < blklen) {
byte1 = edid[index + 1];
byte2 = edid[index + 2];
byte3 = edid[index + 3];
index += 3;
i += 3;
audio_format = byte1 >> 3;
max_ch = (byte1 & 0x07) + 1;
DPRINTK("Audio Format Descriptor : %2d\n", audio_format);
DPRINTK("Max Number of Channels : %2d\n", max_ch);
DPRINTK("Sample Rates : %02x\n", byte2);
/* ALSA can't specify specific compressed
* formats, so only care about PCM for now. */
if (audio_format == AUDIO_CODING_TYPE_LPCM) {
if (max_ch > cfg->max_channels)
cfg->max_channels = max_ch;
cfg->sample_rates |= byte2;
cfg->sample_sizes |= byte3 & 0x7;
DPRINTK("Sample Sizes : %02x\n",
byte3 & 0x7);
}
}
break;
}
case 0x4: /*Speaker allocation block*/
{
i = 0;
while (i < blklen) {
cfg->speaker_alloc = edid[index + 1];
index += 3;
i += 3;
DPRINTK("Speaker Alloc : %02x\n", cfg->speaker_alloc);
}
break;
}
case 0x7: /*User extended block*/
default:
/* skip */
DPRINTK("Not handle block, tagcode = 0x%x\n", tagcode);
index += blklen;
break;
}
index++;
}
}
/* long desc */
DPRINTK("CEA long desc timmings\n");
index = detail_timing_desc_offset;
block = edid + index;
while (index < (EDID_LENGTH - DETAILED_TIMING_DESCRIPTION_SIZE)) {
if (!(block[0] == 0x00 && block[1] == 0x00)) {
get_detailed_timing(block, &mode[num]);
num++;
}
block += DETAILED_TIMING_DESCRIPTION_SIZE;
index += DETAILED_TIMING_DESCRIPTION_SIZE;
}
if (!num) {
kfree(mode);
return 0;
}
m = kmalloc((num + specs->modedb_len) *
sizeof(struct fb_videomode), GFP_KERNEL);
if (!m)
return 0;
if (specs->modedb_len) {
memmove(m, specs->modedb,
specs->modedb_len * sizeof(struct fb_videomode));
kfree(specs->modedb);
}
memmove(m+specs->modedb_len, mode,
num * sizeof(struct fb_videomode));
kfree(mode);
specs->modedb_len += num;
specs->modedb = m;
return 0;
}
EXPORT_SYMBOL(mxc_edid_parse_ext_blk);
static int mxc_edid_readblk(struct i2c_adapter *adp,
unsigned short addr, unsigned char *edid)
{
int ret = 0, extblknum = 0;
unsigned char regaddr = 0x0;
struct i2c_msg msg[2] = {
{
.addr = addr,
.flags = 0,
.len = 1,
.buf = &regaddr,
}, {
.addr = addr,
.flags = I2C_M_RD,
.len = EDID_LENGTH,
.buf = edid,
},
};
ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
if (ret != ARRAY_SIZE(msg)) {
DPRINTK("unable to read EDID block\n");
return -EIO;
}
if (edid[1] == 0x00)
return -ENOENT;
extblknum = edid[0x7E];
if (extblknum) {
regaddr = 128;
msg[1].buf = edid + EDID_LENGTH;
ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
if (ret != ARRAY_SIZE(msg)) {
DPRINTK("unable to read EDID ext block\n");
return -EIO;
}
}
return extblknum;
}
static int mxc_edid_readsegblk(struct i2c_adapter *adp, unsigned short addr,
unsigned char *edid, int seg_num)
{
int ret = 0;
unsigned char segment = 0x1, regaddr = 0;
struct i2c_msg msg[3] = {
{
.addr = 0x30,
.flags = 0,
.len = 1,
.buf = &segment,
}, {
.addr = addr,
.flags = 0,
.len = 1,
.buf = &regaddr,
}, {
.addr = addr,
.flags = I2C_M_RD,
.len = EDID_LENGTH,
.buf = edid,
},
};
ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
if (ret != ARRAY_SIZE(msg)) {
DPRINTK("unable to read EDID block\n");
return -EIO;
}
if (seg_num == 2) {
regaddr = 128;
msg[2].buf = edid + EDID_LENGTH;
ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
if (ret != ARRAY_SIZE(msg)) {
DPRINTK("unable to read EDID block\n");
return -EIO;
}
}
return ret;
}
int mxc_edid_var_to_vic(struct fb_var_screeninfo *var)
{
int i;
struct fb_videomode m;
for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
fb_var_to_videomode(&m, var);
if (mxc_edid_fb_mode_is_equal(false, &m, &mxc_cea_mode[i]))
break;
}
if (i == ARRAY_SIZE(mxc_cea_mode))
return 0;
return i;
}
EXPORT_SYMBOL(mxc_edid_var_to_vic);
int mxc_edid_mode_to_vic(const struct fb_videomode *mode)
{
int i;
bool use_aspect = (mode->vmode & FB_VMODE_ASPECT_MASK);
for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
if (mxc_edid_fb_mode_is_equal(use_aspect, mode, &mxc_cea_mode[i]))
break;
}
if (i == ARRAY_SIZE(mxc_cea_mode))
return 0;
return i;
}
EXPORT_SYMBOL(mxc_edid_mode_to_vic);
/* make sure edid has 512 bytes*/
int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi)
{
int ret = 0, extblknum;
if (!adp || !edid || !cfg || !fbi)
return -EINVAL;
memset(edid, 0, EDID_LENGTH*4);
memset(cfg, 0, sizeof(struct mxc_edid_cfg));
extblknum = mxc_edid_readblk(adp, addr, edid);
if (extblknum < 0)
return extblknum;
/* edid first block parsing */
memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
fb_edid_to_monspecs(edid, &fbi->monspecs);
if (extblknum) {
int i;
/* FIXME: mxc_edid_readsegblk() won't read more than 2 blocks
* and the for-loop will read past the end of the buffer! :-( */
if (extblknum > 3) {
WARN_ON(true);
return -EINVAL;
}
/* need read segment block? */
if (extblknum > 1) {
ret = mxc_edid_readsegblk(adp, addr,
edid + EDID_LENGTH*2, extblknum - 1);
if (ret < 0)
return ret;
}
for (i = 1; i <= extblknum; i++)
/* edid ext block parsing */
mxc_edid_parse_ext_blk(edid + i*EDID_LENGTH,
cfg, &fbi->monspecs);
}
return 0;
}
EXPORT_SYMBOL(mxc_edid_read);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,237 @@
/*
* Copyright (C) 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/init.h>
#include <linux/ipu.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mxcfb.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include "mxc_dispdrv.h"
struct mxc_lcd_platform_data {
u32 default_ifmt;
u32 ipu_id;
u32 disp_id;
};
struct mxc_lcdif_data {
struct platform_device *pdev;
struct mxc_dispdrv_handle *disp_lcdif;
};
#define DISPDRV_LCD "lcd"
static struct fb_videomode lcdif_modedb[] = {
{
/* 800x480 @ 57 Hz , pixel clk @ 27MHz */
"CLAA-WVGA", 57, 800, 480, 37037, 40, 60, 10, 10, 20, 10,
FB_SYNC_CLK_LAT_FALL,
FB_VMODE_NONINTERLACED,
0,},
{
/* 800x480 @ 60 Hz , pixel clk @ 32MHz */
"SEIKO-WVGA", 60, 800, 480, 29850, 89, 164, 23, 10, 10, 10,
FB_SYNC_CLK_LAT_FALL,
FB_VMODE_NONINTERLACED,
0,},
};
static int lcdif_modedb_sz = ARRAY_SIZE(lcdif_modedb);
static int lcdif_init(struct mxc_dispdrv_handle *disp,
struct mxc_dispdrv_setting *setting)
{
int ret, i;
struct mxc_lcdif_data *lcdif = mxc_dispdrv_getdata(disp);
struct device *dev = &lcdif->pdev->dev;
struct mxc_lcd_platform_data *plat_data = dev->platform_data;
struct fb_videomode *modedb = lcdif_modedb;
int modedb_sz = lcdif_modedb_sz;
/* use platform defined ipu/di */
ret = ipu_di_to_crtc(dev, plat_data->ipu_id,
plat_data->disp_id, &setting->crtc);
if (ret < 0)
return ret;
ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,
modedb, modedb_sz, NULL, setting->default_bpp);
if (!ret) {
fb_videomode_to_var(&setting->fbi->var, &modedb[0]);
setting->if_fmt = plat_data->default_ifmt;
}
INIT_LIST_HEAD(&setting->fbi->modelist);
for (i = 0; i < modedb_sz; i++) {
struct fb_videomode m;
fb_var_to_videomode(&m, &setting->fbi->var);
if (fb_mode_is_equal(&m, &modedb[i])) {
fb_add_videomode(&modedb[i],
&setting->fbi->modelist);
break;
}
}
return ret;
}
void lcdif_deinit(struct mxc_dispdrv_handle *disp)
{
/*TODO*/
}
static struct mxc_dispdrv_driver lcdif_drv = {
.name = DISPDRV_LCD,
.init = lcdif_init,
.deinit = lcdif_deinit,
};
static int lcd_get_of_property(struct platform_device *pdev,
struct mxc_lcd_platform_data *plat_data)
{
struct device_node *np = pdev->dev.of_node;
int err;
u32 ipu_id, disp_id;
const char *default_ifmt;
err = of_property_read_string(np, "default_ifmt", &default_ifmt);
if (err) {
dev_dbg(&pdev->dev, "get of property default_ifmt fail\n");
return err;
}
err = of_property_read_u32(np, "ipu_id", &ipu_id);
if (err) {
dev_dbg(&pdev->dev, "get of property ipu_id fail\n");
return err;
}
err = of_property_read_u32(np, "disp_id", &disp_id);
if (err) {
dev_dbg(&pdev->dev, "get of property disp_id fail\n");
return err;
}
plat_data->ipu_id = ipu_id;
plat_data->disp_id = disp_id;
if (!strncmp(default_ifmt, "RGB24", 5))
plat_data->default_ifmt = IPU_PIX_FMT_RGB24;
else if (!strncmp(default_ifmt, "BGR24", 5))
plat_data->default_ifmt = IPU_PIX_FMT_BGR24;
else if (!strncmp(default_ifmt, "GBR24", 5))
plat_data->default_ifmt = IPU_PIX_FMT_GBR24;
else if (!strncmp(default_ifmt, "RGB565", 6))
plat_data->default_ifmt = IPU_PIX_FMT_RGB565;
else if (!strncmp(default_ifmt, "RGB666", 6))
plat_data->default_ifmt = IPU_PIX_FMT_RGB666;
else if (!strncmp(default_ifmt, "YUV444", 6))
plat_data->default_ifmt = IPU_PIX_FMT_YUV444;
else if (!strncmp(default_ifmt, "LVDS666", 7))
plat_data->default_ifmt = IPU_PIX_FMT_LVDS666;
else if (!strncmp(default_ifmt, "YUYV16", 6))
plat_data->default_ifmt = IPU_PIX_FMT_YUYV;
else if (!strncmp(default_ifmt, "UYVY16", 6))
plat_data->default_ifmt = IPU_PIX_FMT_UYVY;
else if (!strncmp(default_ifmt, "YVYU16", 6))
plat_data->default_ifmt = IPU_PIX_FMT_YVYU;
else if (!strncmp(default_ifmt, "VYUY16", 6))
plat_data->default_ifmt = IPU_PIX_FMT_VYUY;
else {
dev_err(&pdev->dev, "err default_ifmt!\n");
return -ENOENT;
}
return err;
}
static int mxc_lcdif_probe(struct platform_device *pdev)
{
int ret;
struct pinctrl *pinctrl;
struct mxc_lcdif_data *lcdif;
struct mxc_lcd_platform_data *plat_data;
dev_dbg(&pdev->dev, "%s enter\n", __func__);
lcdif = devm_kzalloc(&pdev->dev, sizeof(struct mxc_lcdif_data),
GFP_KERNEL);
if (!lcdif)
return -ENOMEM;
plat_data = devm_kzalloc(&pdev->dev,
sizeof(struct mxc_lcd_platform_data),
GFP_KERNEL);
if (!plat_data)
return -ENOMEM;
pdev->dev.platform_data = plat_data;
ret = lcd_get_of_property(pdev, plat_data);
if (ret < 0) {
dev_err(&pdev->dev, "get lcd of property fail\n");
return ret;
}
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
dev_err(&pdev->dev, "can't get/select pinctrl\n");
return PTR_ERR(pinctrl);
}
lcdif->pdev = pdev;
lcdif->disp_lcdif = mxc_dispdrv_register(&lcdif_drv);
mxc_dispdrv_setdata(lcdif->disp_lcdif, lcdif);
dev_set_drvdata(&pdev->dev, lcdif);
dev_dbg(&pdev->dev, "%s exit\n", __func__);
return ret;
}
static int mxc_lcdif_remove(struct platform_device *pdev)
{
struct mxc_lcdif_data *lcdif = dev_get_drvdata(&pdev->dev);
mxc_dispdrv_puthandle(lcdif->disp_lcdif);
mxc_dispdrv_unregister(lcdif->disp_lcdif);
kfree(lcdif);
return 0;
}
static const struct of_device_id imx_lcd_dt_ids[] = {
{ .compatible = "fsl,lcd"},
{ /* sentinel */ }
};
static struct platform_driver mxc_lcdif_driver = {
.driver = {
.name = "mxc_lcdif",
.of_match_table = imx_lcd_dt_ids,
},
.probe = mxc_lcdif_probe,
.remove = mxc_lcdif_remove,
};
static int __init mxc_lcdif_init(void)
{
return platform_driver_register(&mxc_lcdif_driver);
}
static void __exit mxc_lcdif_exit(void)
{
platform_driver_unregister(&mxc_lcdif_driver);
}
module_init(mxc_lcdif_init);
module_exit(mxc_lcdif_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("i.MX ipuv3 LCD extern port driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __LINUX_IPU_V3_PRE_H_
#define __LINUX_IPU_V3_PRE_H_
#define IPU_PRE_MAX_WIDTH 1920
#define IPU_PRE_MAX_BPP 4
struct ipu_rect {
int left;
int top;
int width;
int height;
};
struct ipu_pre_context {
bool repeat;
bool vflip;
bool handshake_en;
bool hsk_abort_en;
unsigned int hsk_line_num;
bool sdw_update;
unsigned int block_size;
unsigned int interlaced;
unsigned int prefetch_mode;
unsigned long cur_buf;
unsigned long next_buf;
unsigned int tile_fmt;
unsigned int read_burst;
unsigned int prefetch_input_bpp;
unsigned int prefetch_input_pixel_fmt;
unsigned int prefetch_shift_offset;
unsigned int prefetch_shift_width;
bool shift_bypass;
bool field_inverse;
bool tpr_coor_offset_en;
/* the output of prefetch is
* also the input of store
*/
struct ipu_rect prefetch_output_size;
unsigned int prefetch_input_active_width;
unsigned int prefetch_input_width;
unsigned int prefetch_input_height;
unsigned int store_pitch;
int interlace_offset;
bool store_en;
unsigned int write_burst;
unsigned int store_output_bpp;
unsigned int sec_buf_off;
unsigned int trd_buf_off;
/* return for IPU fb caller */
unsigned long store_addr;
};
#ifdef CONFIG_MXC_IPU_V3_PRE
int ipu_pre_alloc(int ipu_id, ipu_channel_t ipu_ch);
void ipu_pre_free(unsigned int *id);
unsigned long ipu_pre_alloc_double_buffer(unsigned int id, unsigned int size);
void ipu_pre_free_double_buffer(unsigned int id);
int ipu_pre_config(int id, struct ipu_pre_context *config);
int ipu_pre_enable(int id);
void ipu_pre_disable(int id);
int ipu_pre_set_fb_buffer(int id, unsigned long fb_paddr,
unsigned int x_crop,
unsigned int y_crop,
unsigned int sec_buf_off,
unsigned int trd_buf_off);
int ipu_pre_sdw_update(int id);
#else
int ipu_pre_alloc(int ipu_id, ipu_channel_t channel)
{
return -ENODEV;
}
void ipu_pre_free(unsigned int *id)
{
}
unsigned long ipu_pre_alloc_double_buffer(unsigned int id, unsigned int size)
{
return -ENODEV;
}
void ipu_pre_free_double_buffer(unsigned int id)
{
}
int ipu_pre_config(int id, struct ipu_pre_context *config)
{
return -ENODEV;
}
int ipu_pre_enable(int id)
{
return -ENODEV;
}
void ipu_pre_disable(int id)
{
return;
}
int ipu_pre_set_fb_buffer(int id, unsigned long fb_paddr,
unsigned int x_crop,
unsigned int y_crop,
unsigned int sec_buf_off,
unsigned int trd_buf_off)
{
return -ENODEV;
}
int ipu_pre_sdw_update(int id)
{
return -ENODEV;
}
#endif
#endif /* __LINUX_IPU_V3_PRE_H_ */

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __LINUX_IPU_V3_PRG_H_
#define __LINUX_IPU_V3_PRG_H_
#include <linux/ipu-v3.h>
#define PRG_SO_INTERLACE 1
#define PRG_SO_PROGRESSIVE 0
#define PRG_BLOCK_MODE 1
#define PRG_SCAN_MODE 0
struct ipu_prg_config {
unsigned int id;
unsigned int pre_num;
ipu_channel_t ipu_ch;
unsigned int stride;
unsigned int height;
unsigned int ipu_height;
unsigned int crop_line;
unsigned int so;
unsigned int ilo;
unsigned int block_mode;
bool vflip;
u32 baddr;
u32 offset;
};
#ifdef CONFIG_MXC_IPU_V3_PRG
int ipu_prg_config(struct ipu_prg_config *config);
int ipu_prg_disable(unsigned int ipu_id, unsigned int pre_num);
int ipu_prg_wait_buf_ready(unsigned int ipu_id, unsigned int pre_num,
unsigned int hsk_line_num,
int pre_store_out_height);
#else
int ipu_prg_config(struct ipu_prg_config *config)
{
return -ENODEV;
}
int ipu_prg_disable(unsigned int ipu_id, unsigned int pre_num)
{
return -ENODEV;
}
int ipu_prg_wait_buf_ready(unsigned int ipu_id, unsigned int pre_num,
unsigned int hsk_line_num,
int pre_store_out_height)
{
return -ENODEV;
}
#endif
#endif /* __LINUX_IPU_V3_PRG_H_ */

View File

@ -0,0 +1,770 @@
/*
* Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
* Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* 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 __LINUX_IPU_V3_H_
#define __LINUX_IPU_V3_H_
#include <linux/ipu.h>
/* IPU Driver channels definitions. */
/* Note these are different from IDMA channels */
#define IPU_MAX_CH 32
#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \
((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)
#define _MAKE_ALT_CHAN(ch) (ch | (IPU_MAX_CH << 24))
#define IPU_CHAN_ID(ch) (ch >> 24)
#define IPU_CHAN_ALT(ch) (ch & 0x02000000)
#define IPU_CHAN_ALPHA_IN_DMA(ch) ((uint32_t) (ch >> 6) & 0x3F)
#define IPU_CHAN_GRAPH_IN_DMA(ch) ((uint32_t) (ch >> 12) & 0x3F)
#define IPU_CHAN_VIDEO_IN_DMA(ch) ((uint32_t) (ch >> 18) & 0x3F)
#define IPU_CHAN_OUT_DMA(ch) ((uint32_t) (ch & 0x3F))
#define NO_DMA 0x3F
#define ALT 1
/*!
* Enumeration of IPU logical channels. An IPU logical channel is defined as a
* combination of an input (memory to IPU), output (IPU to memory), and/or
* secondary input IDMA channels and in some cases an Image Converter task.
* Some channels consist of only an input or output.
*/
typedef enum {
CHAN_NONE = -1,
MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48),
MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49),
MEM_ROT_PP_MEM = _MAKE_CHAN(3, 47, NO_DMA, NO_DMA, 50),
MEM_PRP_ENC_MEM = _MAKE_CHAN(4, 12, 14, 17, 20),
MEM_PRP_VF_MEM = _MAKE_CHAN(5, 12, 14, 17, 21),
MEM_PP_MEM = _MAKE_CHAN(6, 11, 15, 18, 22),
MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA),
MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA),
MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA),
MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA),
MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA),
MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA),
MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0),
MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0),
DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
CSI_MEM0 = _MAKE_CHAN(15, NO_DMA, NO_DMA, NO_DMA, 0),
CSI_MEM1 = _MAKE_CHAN(16, NO_DMA, NO_DMA, NO_DMA, 1),
CSI_MEM2 = _MAKE_CHAN(17, NO_DMA, NO_DMA, NO_DMA, 2),
CSI_MEM3 = _MAKE_CHAN(18, NO_DMA, NO_DMA, NO_DMA, 3),
CSI_MEM = CSI_MEM0,
CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20),
CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21),
/* for vdi mem->vdi->ic->mem , add graphics plane and alpha*/
MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21),
MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21),
MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21),
/* for vdi mem->vdi->mem */
MEM_VDI_MEM_P = _MAKE_CHAN(24, 8, NO_DMA, NO_DMA, 5),
MEM_VDI_MEM = _MAKE_CHAN(25, 9, NO_DMA, NO_DMA, 5),
MEM_VDI_MEM_N = _MAKE_CHAN(26, 10, NO_DMA, NO_DMA, 5),
/* fake channel for vdoa to link with IPU */
MEM_VDOA_MEM = _MAKE_CHAN(27, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
MEM_PP_ADC = CHAN_NONE,
ADC_SYS2 = CHAN_NONE,
} ipu_channel_t;
/*!
* Enumeration of types of buffers for a logical channel.
*/
typedef enum {
IPU_OUTPUT_BUFFER = 0, /*!< Buffer for output from IPU */
IPU_ALPHA_IN_BUFFER = 1, /*!< Buffer for input to IPU */
IPU_GRAPH_IN_BUFFER = 2, /*!< Buffer for input to IPU */
IPU_VIDEO_IN_BUFFER = 3, /*!< Buffer for input to IPU */
IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER,
IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER,
} ipu_buffer_t;
#define IPU_PANEL_SERIAL 1
#define IPU_PANEL_PARALLEL 2
/*!
* Enumeration of ADC channel operation mode.
*/
typedef enum {
Disable,
WriteTemplateNonSeq,
ReadTemplateNonSeq,
WriteTemplateUnCon,
ReadTemplateUnCon,
WriteDataWithRS,
WriteDataWoRS,
WriteCmd
} mcu_mode_t;
/*!
* Enumeration of ADC channel addressing mode.
*/
typedef enum {
FullWoBE,
FullWithBE,
XY
} display_addressing_t;
/*!
* Union of initialization parameters for a logical channel.
*/
typedef union {
struct {
uint32_t csi;
uint32_t mipi_id;
uint32_t mipi_vc;
bool mipi_en;
bool interlaced;
} csi_mem;
struct {
uint32_t in_width;
uint32_t in_height;
uint32_t in_pixel_fmt;
uint32_t out_width;
uint32_t out_height;
uint32_t out_pixel_fmt;
uint32_t outh_resize_ratio;
uint32_t outv_resize_ratio;
uint32_t csi;
uint32_t mipi_id;
uint32_t mipi_vc;
bool mipi_en;
} csi_prp_enc_mem;
struct {
uint32_t in_width;
uint32_t in_height;
uint32_t in_pixel_fmt;
uint32_t out_width;
uint32_t out_height;
uint32_t out_pixel_fmt;
uint32_t outh_resize_ratio;
uint32_t outv_resize_ratio;
} mem_prp_enc_mem;
struct {
uint32_t in_width;
uint32_t in_height;
uint32_t in_pixel_fmt;
uint32_t out_width;
uint32_t out_height;
uint32_t out_pixel_fmt;
} mem_rot_enc_mem;
struct {
uint32_t in_width;
uint32_t in_height;
uint32_t in_pixel_fmt;
uint32_t out_width;
uint32_t out_height;
uint32_t out_pixel_fmt;
uint32_t outh_resize_ratio;
uint32_t outv_resize_ratio;
bool graphics_combine_en;
bool global_alpha_en;
bool key_color_en;
uint32_t in_g_pixel_fmt;
uint8_t alpha;
uint32_t key_color;
bool alpha_chan_en;
ipu_motion_sel motion_sel;
enum v4l2_field field_fmt;
uint32_t csi;
uint32_t mipi_id;
uint32_t mipi_vc;
bool mipi_en;
} csi_prp_vf_mem;
struct {
uint32_t in_width;
uint32_t in_height;
uint32_t in_pixel_fmt;
uint32_t out_width;
uint32_t out_height;
uint32_t out_pixel_fmt;
bool graphics_combine_en;
bool global_alpha_en;
bool key_color_en;
display_port_t disp;
uint32_t out_left;
uint32_t out_top;
} csi_prp_vf_adc;
struct {
uint32_t in_width;
uint32_t in_height;
uint32_t in_pixel_fmt;
uint32_t out_width;
uint32_t out_height;
uint32_t out_pixel_fmt;
uint32_t outh_resize_ratio;
uint32_t outv_resize_ratio;
bool graphics_combine_en;
bool global_alpha_en;
bool key_color_en;
uint32_t in_g_pixel_fmt;
uint8_t alpha;
uint32_t key_color;
bool alpha_chan_en;
ipu_motion_sel motion_sel;
enum v4l2_field field_fmt;
} mem_prp_vf_mem;
struct {
uint32_t temp;
} mem_prp_vf_adc;
struct {
uint32_t temp;
} mem_rot_vf_mem;
struct {
uint32_t in_width;
uint32_t in_height;
uint32_t in_pixel_fmt;
uint32_t out_width;
uint32_t out_height;
uint32_t out_pixel_fmt;
uint32_t outh_resize_ratio;
uint32_t outv_resize_ratio;
bool graphics_combine_en;
bool global_alpha_en;
bool key_color_en;
uint32_t in_g_pixel_fmt;
uint8_t alpha;
uint32_t key_color;
bool alpha_chan_en;
} mem_pp_mem;
struct {
uint32_t temp;
} mem_rot_mem;
struct {
uint32_t in_width;
uint32_t in_height;
uint32_t in_pixel_fmt;
uint32_t out_width;
uint32_t out_height;
uint32_t out_pixel_fmt;
bool graphics_combine_en;
bool global_alpha_en;
bool key_color_en;
display_port_t disp;
uint32_t out_left;
uint32_t out_top;
} mem_pp_adc;
struct {
uint32_t di;
bool interlaced;
uint32_t in_pixel_fmt;
uint32_t out_pixel_fmt;
} mem_dc_sync;
struct {
uint32_t temp;
} mem_sdc_fg;
struct {
uint32_t di;
bool interlaced;
uint32_t in_pixel_fmt;
uint32_t out_pixel_fmt;
bool alpha_chan_en;
} mem_dp_bg_sync;
struct {
uint32_t temp;
} mem_sdc_bg;
struct {
uint32_t di;
bool interlaced;
uint32_t in_pixel_fmt;
uint32_t out_pixel_fmt;
bool alpha_chan_en;
} mem_dp_fg_sync;
struct {
uint32_t di;
} direct_async;
struct {
display_port_t disp;
mcu_mode_t ch_mode;
uint32_t out_left;
uint32_t out_top;
} adc_sys1;
struct {
display_port_t disp;
mcu_mode_t ch_mode;
uint32_t out_left;
uint32_t out_top;
} adc_sys2;
} ipu_channel_params_t;
/*
* IPU_IRQF_ONESHOT - Interrupt is not reenabled after the irq handler finished.
*/
#define IPU_IRQF_NONE 0x00000000
#define IPU_IRQF_ONESHOT 0x00000001
/*!
* Enumeration of IPU interrupt sources.
*/
enum ipu_irq_line {
IPU_IRQ_CSI0_OUT_EOF = 0,
IPU_IRQ_CSI1_OUT_EOF = 1,
IPU_IRQ_CSI2_OUT_EOF = 2,
IPU_IRQ_CSI3_OUT_EOF = 3,
IPU_IRQ_VDIC_OUT_EOF = 5,
IPU_IRQ_VDI_P_IN_EOF = 8,
IPU_IRQ_VDI_C_IN_EOF = 9,
IPU_IRQ_VDI_N_IN_EOF = 10,
IPU_IRQ_PP_IN_EOF = 11,
IPU_IRQ_PRP_IN_EOF = 12,
IPU_IRQ_PRP_GRAPH_IN_EOF = 14,
IPU_IRQ_PP_GRAPH_IN_EOF = 15,
IPU_IRQ_PRP_ALPHA_IN_EOF = 17,
IPU_IRQ_PP_ALPHA_IN_EOF = 18,
IPU_IRQ_PRP_ENC_OUT_EOF = 20,
IPU_IRQ_PRP_VF_OUT_EOF = 21,
IPU_IRQ_PP_OUT_EOF = 22,
IPU_IRQ_BG_SYNC_EOF = 23,
IPU_IRQ_BG_ASYNC_EOF = 24,
IPU_IRQ_FG_SYNC_EOF = 27,
IPU_IRQ_DC_SYNC_EOF = 28,
IPU_IRQ_FG_ASYNC_EOF = 29,
IPU_IRQ_FG_ALPHA_SYNC_EOF = 31,
IPU_IRQ_FG_ALPHA_ASYNC_EOF = 33,
IPU_IRQ_DC_READ_EOF = 40,
IPU_IRQ_DC_ASYNC_EOF = 41,
IPU_IRQ_DC_CMD1_EOF = 42,
IPU_IRQ_DC_CMD2_EOF = 43,
IPU_IRQ_DC_MASK_EOF = 44,
IPU_IRQ_PRP_ENC_ROT_IN_EOF = 45,
IPU_IRQ_PRP_VF_ROT_IN_EOF = 46,
IPU_IRQ_PP_ROT_IN_EOF = 47,
IPU_IRQ_PRP_ENC_ROT_OUT_EOF = 48,
IPU_IRQ_PRP_VF_ROT_OUT_EOF = 49,
IPU_IRQ_PP_ROT_OUT_EOF = 50,
IPU_IRQ_BG_ALPHA_SYNC_EOF = 51,
IPU_IRQ_BG_ALPHA_ASYNC_EOF = 52,
IPU_IRQ_BG_SYNC_NFACK = 64 + 23,
IPU_IRQ_FG_SYNC_NFACK = 64 + 27,
IPU_IRQ_DC_SYNC_NFACK = 64 + 28,
IPU_IRQ_DP_SF_START = 448 + 2,
IPU_IRQ_DP_SF_END = 448 + 3,
IPU_IRQ_BG_SF_END = IPU_IRQ_DP_SF_END,
IPU_IRQ_DC_FC_0 = 448 + 8,
IPU_IRQ_DC_FC_1 = 448 + 9,
IPU_IRQ_DC_FC_2 = 448 + 10,
IPU_IRQ_DC_FC_3 = 448 + 11,
IPU_IRQ_DC_FC_4 = 448 + 12,
IPU_IRQ_DC_FC_6 = 448 + 13,
IPU_IRQ_VSYNC_PRE_0 = 448 + 14,
IPU_IRQ_VSYNC_PRE_1 = 448 + 15,
IPU_IRQ_COUNT
};
/*!
* Bitfield of Display Interface signal polarities.
*/
typedef struct {
unsigned datamask_en:1;
unsigned int_clk:1;
unsigned interlaced:1;
unsigned odd_field_first:1;
unsigned clksel_en:1;
unsigned clkidle_en:1;
unsigned data_pol:1; /* true = inverted */
unsigned clk_pol:1; /* true = rising edge */
unsigned enable_pol:1;
unsigned Hsync_pol:1; /* true = active high */
unsigned Vsync_pol:1;
} ipu_di_signal_cfg_t;
/*!
* Bitfield of CSI signal polarities and modes.
*/
typedef struct {
unsigned data_width:4;
unsigned clk_mode:3;
unsigned ext_vsync:1;
unsigned Vsync_pol:1;
unsigned Hsync_pol:1;
unsigned pixclk_pol:1;
unsigned data_pol:1;
unsigned sens_clksrc:1;
unsigned pack_tight:1;
unsigned force_eof:1;
unsigned data_en_pol:1;
unsigned data_fmt;
unsigned csi;
unsigned mclk;
} ipu_csi_signal_cfg_t;
/*!
* Enumeration of CSI data bus widths.
*/
enum {
IPU_CSI_DATA_WIDTH_4 = 0,
IPU_CSI_DATA_WIDTH_8 = 1,
IPU_CSI_DATA_WIDTH_10 = 3,
IPU_CSI_DATA_WIDTH_16 = 9,
};
/*!
* Enumeration of CSI clock modes.
*/
enum {
IPU_CSI_CLK_MODE_GATED_CLK,
IPU_CSI_CLK_MODE_NONGATED_CLK,
IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE,
IPU_CSI_CLK_MODE_CCIR656_INTERLACED,
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR,
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR,
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR,
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR,
};
enum {
IPU_CSI_MIPI_DI0,
IPU_CSI_MIPI_DI1,
IPU_CSI_MIPI_DI2,
IPU_CSI_MIPI_DI3,
};
typedef enum {
RGB,
YCbCr,
YUV
} ipu_color_space_t;
/*!
* Enumeration of ADC vertical sync mode.
*/
typedef enum {
VsyncNone,
VsyncInternal,
VsyncCSI,
VsyncExternal
} vsync_t;
typedef enum {
DAT,
CMD
} cmddata_t;
/*!
* Enumeration of ADC display update mode.
*/
typedef enum {
IPU_ADC_REFRESH_NONE,
IPU_ADC_AUTO_REFRESH,
IPU_ADC_AUTO_REFRESH_SNOOP,
IPU_ADC_SNOOPING,
} ipu_adc_update_mode_t;
/*!
* Enumeration of ADC display interface types (serial or parallel).
*/
enum {
IPU_ADC_IFC_MODE_SYS80_TYPE1,
IPU_ADC_IFC_MODE_SYS80_TYPE2,
IPU_ADC_IFC_MODE_SYS68K_TYPE1,
IPU_ADC_IFC_MODE_SYS68K_TYPE2,
IPU_ADC_IFC_MODE_3WIRE_SERIAL,
IPU_ADC_IFC_MODE_4WIRE_SERIAL,
IPU_ADC_IFC_MODE_5WIRE_SERIAL_CLK,
IPU_ADC_IFC_MODE_5WIRE_SERIAL_CS,
};
enum {
IPU_ADC_IFC_WIDTH_8,
IPU_ADC_IFC_WIDTH_16,
};
/*!
* Enumeration of ADC display interface burst mode.
*/
enum {
IPU_ADC_BURST_WCS,
IPU_ADC_BURST_WBLCK,
IPU_ADC_BURST_NONE,
IPU_ADC_BURST_SERIAL,
};
/*!
* Enumeration of ADC display interface RW signal timing modes.
*/
enum {
IPU_ADC_SER_NO_RW,
IPU_ADC_SER_RW_BEFORE_RS,
IPU_ADC_SER_RW_AFTER_RS,
};
/*!
* Bitfield of ADC signal polarities and modes.
*/
typedef struct {
unsigned data_pol:1;
unsigned clk_pol:1;
unsigned cs_pol:1;
unsigned rs_pol:1;
unsigned addr_pol:1;
unsigned read_pol:1;
unsigned write_pol:1;
unsigned Vsync_pol:1;
unsigned burst_pol:1;
unsigned burst_mode:2;
unsigned ifc_mode:3;
unsigned ifc_width:5;
unsigned ser_preamble_len:4;
unsigned ser_preamble:8;
unsigned ser_rw_mode:2;
} ipu_adc_sig_cfg_t;
/*!
* Enumeration of ADC template commands.
*/
enum {
RD_DATA,
RD_ACK,
RD_WAIT,
WR_XADDR,
WR_YADDR,
WR_ADDR,
WR_CMND,
WR_DATA,
};
/*!
* Enumeration of ADC template command flow control.
*/
enum {
SINGLE_STEP,
PAUSE,
STOP,
};
/*Define template constants*/
#define ATM_ADDR_RANGE 0x20 /*offset address of DISP */
#define TEMPLATE_BUF_SIZE 0x20 /*size of template */
/*!
* Define to create ADC template command entry.
*/
#define ipu_adc_template_gen(oc, rs, fc, dat) (((rs) << 29) | ((fc) << 27) | \
((oc) << 24) | (dat))
typedef struct {
u32 reg;
u32 value;
} ipu_lpmc_reg_t;
#define IPU_LPMC_REG_READ 0x80000000L
#define CSI_MCLK_VF 1
#define CSI_MCLK_ENC 2
#define CSI_MCLK_RAW 4
#define CSI_MCLK_I2C 8
struct ipu_soc;
/* Common IPU API */
struct ipu_soc *ipu_get_soc(int id);
int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params);
void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel);
void ipu_disable_hsp_clk(struct ipu_soc *ipu);
static inline bool ipu_can_rotate_in_place(ipu_rotate_mode_t rot)
{
#ifdef CONFIG_MXC_IPU_V3D
return (rot < IPU_ROTATE_HORIZ_FLIP);
#else
return (rot < IPU_ROTATE_90_RIGHT);
#endif
}
int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
uint32_t pixel_fmt,
uint16_t width, uint16_t height,
uint32_t stride,
ipu_rotate_mode_t rot_mode,
dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
dma_addr_t phyaddr_2,
uint32_t u_offset, uint32_t v_offset);
int32_t ipu_update_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
uint32_t bufNum, dma_addr_t phyaddr);
int32_t ipu_update_channel_offset(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
uint32_t pixel_fmt,
uint16_t width, uint16_t height,
uint32_t stride,
uint32_t u, uint32_t v,
uint32_t vertical_offset, uint32_t horizontal_offset);
int32_t ipu_get_channel_offset(uint32_t pixel_fmt,
uint16_t width, uint16_t height,
uint32_t stride,
uint32_t u, uint32_t v,
uint32_t vertical_offset, uint32_t horizontal_offset,
uint32_t *u_offset, uint32_t *v_offset);
int32_t ipu_select_buffer(struct ipu_soc *ipu, ipu_channel_t channel,
ipu_buffer_t type, uint32_t bufNum);
int32_t ipu_select_multi_vdi_buffer(struct ipu_soc *ipu, uint32_t bufNum);
int32_t ipu_link_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch);
int32_t ipu_unlink_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch);
int32_t ipu_is_channel_busy(struct ipu_soc *ipu, ipu_channel_t channel);
int32_t ipu_check_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
uint32_t bufNum);
void ipu_clear_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
uint32_t bufNum);
uint32_t ipu_get_cur_buffer_idx(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type);
int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel);
int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wait_for_stop);
int32_t ipu_swap_channel(struct ipu_soc *ipu, ipu_channel_t from_ch, ipu_channel_t to_ch);
uint32_t ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel);
int32_t ipu_enable_csi(struct ipu_soc *ipu, uint32_t csi);
int32_t ipu_disable_csi(struct ipu_soc *ipu, uint32_t csi);
int ipu_lowpwr_display_enable(void);
int ipu_lowpwr_display_disable(void);
int ipu_enable_irq(struct ipu_soc *ipu, uint32_t irq);
void ipu_disable_irq(struct ipu_soc *ipu, uint32_t irq);
void ipu_clear_irq(struct ipu_soc *ipu, uint32_t irq);
int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq,
irqreturn_t(*handler) (int, void *),
uint32_t irq_flags, const char *devname, void *dev_id);
void ipu_free_irq(struct ipu_soc *ipu, uint32_t irq, void *dev_id);
bool ipu_get_irq_status(struct ipu_soc *ipu, uint32_t irq);
void ipu_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3]);
int32_t ipu_set_channel_bandmode(struct ipu_soc *ipu, ipu_channel_t channel,
ipu_buffer_t type, uint32_t band_height);
/* two stripe calculations */
struct stripe_param{
unsigned int input_width; /* width of the input stripe */
unsigned int output_width; /* width of the output stripe */
unsigned int input_column; /* the first column on the input stripe */
unsigned int output_column; /* the first column on the output stripe */
unsigned int idr;
/* inverse downisizing ratio parameter; expressed as a power of 2 */
unsigned int irr;
/* inverse resizing ratio parameter; expressed as a multiple of 2^-13 */
};
int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
unsigned int output_frame_width,
const unsigned int maximal_stripe_width,
const unsigned long long cirr,
const unsigned int equal_stripes,
u32 input_pixelformat,
u32 output_pixelformat,
struct stripe_param *left,
struct stripe_param *right);
/* SDC API */
int32_t ipu_init_sync_panel(struct ipu_soc *ipu, int disp,
uint32_t pixel_clk,
uint16_t width, uint16_t height,
uint32_t pixel_fmt,
uint16_t h_start_width, uint16_t h_sync_width,
uint16_t h_end_width, uint16_t v_start_width,
uint16_t v_sync_width, uint16_t v_end_width,
uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig);
void ipu_uninit_sync_panel(struct ipu_soc *ipu, int disp);
int32_t ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, int16_t x_pos,
int16_t y_pos);
int32_t ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, int16_t *x_pos,
int16_t *y_pos);
int32_t ipu_disp_set_global_alpha(struct ipu_soc *ipu, ipu_channel_t channel, bool enable,
uint8_t alpha);
int32_t ipu_disp_set_color_key(struct ipu_soc *ipu, ipu_channel_t channel, bool enable,
uint32_t colorKey);
int32_t ipu_disp_set_gamma_correction(struct ipu_soc *ipu, ipu_channel_t channel, bool enable,
int constk[], int slopek[]);
int ipu_init_async_panel(struct ipu_soc *ipu, int disp, int type, uint32_t cycle_time,
uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig);
void ipu_reset_disp_panel(struct ipu_soc *ipu);
/* CMOS Sensor Interface API */
int32_t ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height,
uint32_t pixel_fmt, ipu_csi_signal_cfg_t sig);
int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi);
int32_t ipu_csi_enable_mclk(struct ipu_soc *ipu, int src, bool flag, bool wait);
static inline int32_t ipu_csi_enable_mclk_if(struct ipu_soc *ipu, int src, uint32_t csi,
bool flag, bool wait)
{
return ipu_csi_enable_mclk(ipu, csi, flag, wait);
}
int ipu_csi_read_mclk_flag(void);
void ipu_csi_flash_strobe(bool flag);
void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t *height, uint32_t csi);
void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi);
void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi);
uint32_t bytes_per_pixel(uint32_t fmt);
bool ipu_ch_param_bad_alpha_pos(uint32_t fmt);
int ipu_ch_param_get_axi_id(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type);
ipu_color_space_t format_to_colorspace(uint32_t fmt);
bool ipu_pixel_format_is_gpu_tile(uint32_t fmt);
bool ipu_pixel_format_is_split_gpu_tile(uint32_t fmt);
bool ipu_pixel_format_is_pre_yuv(uint32_t fmt);
bool ipu_pixel_format_is_multiplanar_yuv(uint32_t fmt);
struct ipuv3_fb_platform_data {
char disp_dev[32];
u32 interface_pix_fmt;
char *mode_str;
int default_bpp;
bool int_clk;
/* reserved mem */
resource_size_t res_base[2];
resource_size_t res_size[2];
/*
* Late init to avoid display channel being
* re-initialized as we've probably setup the
* channel in bootloader.
*/
bool late_init;
/* Enable prefetch engine or not? */
bool prefetch;
/* Enable the PRE resolve engine or not? */
bool resolve;
};
#endif /* __LINUX_IPU_V3_H_ */

View File

@ -0,0 +1,38 @@
/*
* Copyright 2005-2015 Freescale Semiconductor, Inc.
*/
/*
* The code contained herein is licensed under the GNU Lesser General
* Public License. You may obtain a copy of the GNU Lesser General
* Public License Version 2.1 or later at the following locations:
*
* http://www.opensource.org/licenses/lgpl-license.html
* http://www.gnu.org/copyleft/lgpl.html
*/
/*!
* @defgroup IPU MXC Image Processing Unit (IPU) Driver
*/
/*!
* @file linux/ipu.h
*
* @brief This file contains the IPU driver API declarations.
*
* @ingroup IPU
*/
#ifndef __LINUX_IPU_H__
#define __LINUX_IPU_H__
#include <linux/interrupt.h>
#include <uapi/linux/ipu.h>
unsigned int fmt_to_bpp(unsigned int pixelformat);
cs_t colorspaceofpixel(int fmt);
int need_csc(int ifmt, int ofmt);
int ipu_queue_task(struct ipu_task *task);
int ipu_check_task(struct ipu_task *task);
#endif

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __LINUX_MXC_HDMI_CORE_H_
#define __LINUX_MXC_HDMI_CORE_H_
#include <video/mxc_edid.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#define IRQ_DISABLE_SUCCEED 0
#define IRQ_DISABLE_FAIL 1
bool hdmi_check_overflow(void);
u8 hdmi_readb(unsigned int reg);
void hdmi_writeb(u8 value, unsigned int reg);
void hdmi_mask_writeb(u8 data, unsigned int addr, u8 shift, u8 mask);
unsigned int hdmi_read4(unsigned int reg);
void hdmi_write4(unsigned int value, unsigned int reg);
void hdmi_irq_init(void);
void hdmi_irq_enable(int irq);
unsigned int hdmi_irq_disable(int irq);
void hdmi_set_sample_rate(unsigned int rate);
void hdmi_set_dma_mode(unsigned int dma_running);
void hdmi_init_clk_regenerator(void);
void hdmi_clk_regenerator_update_pixel_clock(u32 pixclock);
void hdmi_set_edid_cfg(struct mxc_edid_cfg *cfg);
void hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg);
extern int mxc_hdmi_ipu_id;
extern int mxc_hdmi_disp_id;
void hdmi_set_registered(int registered);
int hdmi_get_registered(void);
int mxc_hdmi_abort_stream(void);
int mxc_hdmi_register_audio(struct snd_pcm_substream *substream);
void mxc_hdmi_unregister_audio(struct snd_pcm_substream *substream);
unsigned int hdmi_set_cable_state(unsigned int state);
unsigned int hdmi_set_blank_state(unsigned int state);
int check_hdmi_state(void);
#endif

View File

@ -244,6 +244,22 @@
#define IMX6Q_GPR5_L2_CLK_STOP BIT(8)
#define IMX6Q_GPR5_ENET_TX_CLK_SEL BIT(9)
#define IMX6Q_GPR5_PRE_PRG_SEL0_MASK (0x3 << 12)
#define IMX6Q_GPR5_PRE_PRG_SEL0_SHIFT 12
#define IMX6Q_GPR5_PRE_PRG_SEL0_MSB 13
#define IMX6Q_GPR5_PRE_PRG_SEL0_LSB 12
#define IMX6Q_GPR5_PRE_PRG_SEL0_PRE1_PRG0_CHAN1 (0x0 << 12)
#define IMX6Q_GPR5_PRE_PRG_SEL0_PRE1_PRG0_CHAN2 (0x1 << 12)
#define IMX6Q_GPR5_PRE_PRG_SEL0_PRE1_PRG1_CHAN1 (0x2 << 12)
#define IMX6Q_GPR5_PRE_PRG_SEL0_PRE1_PRG1_CHAN2 (0x3 << 12)
#define IMX6Q_GPR5_PRE_PRG_SEL1_MASK (0x3 << 14)
#define IMX6Q_GPR5_PRE_PRG_SEL1_SHIFT 14
#define IMX6Q_GPR5_PRE_PRG_SEL1_MSB 15
#define IMX6Q_GPR5_PRE_PRG_SEL1_LSB 14
#define IMX6Q_GPR5_PRE_PRG_SEL1_PRE2_PRG0_CHAN1 (0x0 << 14)
#define IMX6Q_GPR5_PRE_PRG_SEL1_PRE2_PRG0_CHAN2 (0x1 << 14)
#define IMX6Q_GPR5_PRE_PRG_SEL1_PRE2_PRG1_CHAN1 (0x2 << 14)
#define IMX6Q_GPR5_PRE_PRG_SEL1_PRE2_PRG1_CHAN2 (0x3 << 14)
#define IMX6Q_GPR6_IPU1_ID00_WR_QOS_MASK (0xf << 0)
#define IMX6Q_GPR6_IPU1_ID01_WR_QOS_MASK (0xf << 4)
@ -451,6 +467,16 @@
#define IMX6SX_GPR12_PCIE_RX_EQ_MASK (0x7 << 0)
#define IMX6SX_GPR12_PCIE_RX_EQ_2 (0x2 << 0)
/* For imx6dl iomux gpr register field definitions */
#define IMX6DL_GPR3_LVDS1_MUX_CTL_MASK (0x3 << 8)
#define IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI0 (0x0 << 8)
#define IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI1 (0x1 << 8)
#define IMX6DL_GPR3_LVDS1_MUX_CTL_LCDIF (0x2 << 8)
#define IMX6DL_GPR3_LVDS0_MUX_CTL_MASK (0x3 << 6)
#define IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI0 (0x0 << 6)
#define IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI1 (0x1 << 6)
#define IMX6DL_GPR3_LVDS0_MUX_CTL_LCDIF (0x2 << 6)
/* For imx6ul iomux gpr register field define */
#define IMX6UL_GPR1_ENET1_CLK_DIR (0x1 << 17)
#define IMX6UL_GPR1_ENET2_CLK_DIR (0x1 << 18)

View File

@ -0,0 +1,293 @@
/*
* Copyright (C) 2013-2015 Freescale Semiconductor, Inc. All Rights Reserved
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @defgroup IPU MXC Image Processing Unit (IPU) Driver
*/
/*!
* @file uapi/linux/ipu.h
*
* @brief This file contains the IPU driver API declarations.
*
* @ingroup IPU
*/
#ifndef __ASM_ARCH_IPU_H__
#define __ASM_ARCH_IPU_H__
#include <linux/types.h>
#include <linux/videodev2.h>
#ifndef __KERNEL__
#ifndef __cplusplus
typedef unsigned char bool;
#endif
#define irqreturn_t int
#define dma_addr_t int
#define uint32_t unsigned int
#define uint16_t unsigned short
#define uint8_t unsigned char
#define u32 unsigned int
#define u8 unsigned char
#define __u32 u32
#endif
/*!
* Enumeration of IPU rotation modes
*/
typedef enum {
/* Note the enum values correspond to BAM value */
IPU_ROTATE_NONE = 0,
IPU_ROTATE_VERT_FLIP = 1,
IPU_ROTATE_HORIZ_FLIP = 2,
IPU_ROTATE_180 = 3,
IPU_ROTATE_90_RIGHT = 4,
IPU_ROTATE_90_RIGHT_VFLIP = 5,
IPU_ROTATE_90_RIGHT_HFLIP = 6,
IPU_ROTATE_90_LEFT = 7,
} ipu_rotate_mode_t;
/*!
* Enumeration of VDI MOTION select
*/
typedef enum {
MED_MOTION = 0,
LOW_MOTION = 1,
HIGH_MOTION = 2,
} ipu_motion_sel;
/*!
* Enumeration of DI ports for ADC.
*/
typedef enum {
DISP0,
DISP1,
DISP2,
DISP3
} display_port_t;
/* IPU Pixel format definitions */
/* Four-character-code (FOURCC) */
#define fourcc(a, b, c, d)\
(((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24))
/*!
* @name IPU Pixel Formats
*
* Pixel formats are defined with ASCII FOURCC code. The pixel format codes are
* the same used by V4L2 API.
*/
/*! @{ */
/*! @name GPU Tile Formats */
/*! @{ */
#define IPU_PIX_FMT_GPU32_SB_ST fourcc('5', 'P', '4', 'S') /*!< 32bit split buf 4x4 standard */
#define IPU_PIX_FMT_GPU32_SB_SRT fourcc('5', 'P', '4', 'R') /*!< 32bit split buf 4x4 super */
#define IPU_PIX_FMT_GPU32_ST fourcc('5', 'I', '4', 'S') /*!< 32bit single buf 4x4 standard */
#define IPU_PIX_FMT_GPU32_SRT fourcc('5', 'I', '4', 'R') /*!< 32bit single buf 4x4 super */
#define IPU_PIX_FMT_GPU16_SB_ST fourcc('4', 'P', '8', 'S') /*!< 16bit split buf 8x4 standard */
#define IPU_PIX_FMT_GPU16_SB_SRT fourcc('4', 'P', '8', 'R') /*!< 16bit split buf 8x4 super */
#define IPU_PIX_FMT_GPU16_ST fourcc('4', 'I', '8', 'S') /*!< 16bit single buf 8x4 standard */
#define IPU_PIX_FMT_GPU16_SRT fourcc('4', 'I', '8', 'R') /*!< 16bit single buf 8x4 super */
/*! @{ */
/*! @name Generic or Raw Data Formats */
/*! @{ */
#define IPU_PIX_FMT_GENERIC fourcc('I', 'P', 'U', '0') /*!< IPU Generic Data */
#define IPU_PIX_FMT_GENERIC_32 fourcc('I', 'P', 'U', '1') /*!< IPU Generic Data */
#define IPU_PIX_FMT_GENERIC_16 fourcc('I', 'P', 'U', '2') /*!< IPU Generic Data */
#define IPU_PIX_FMT_LVDS666 fourcc('L', 'V', 'D', '6') /*!< IPU Generic Data */
#define IPU_PIX_FMT_LVDS888 fourcc('L', 'V', 'D', '8') /*!< IPU Generic Data */
/*! @} */
/*! @name RGB Formats */
/*! @{ */
#define IPU_PIX_FMT_RGB332 fourcc('R', 'G', 'B', '1') /*!< 8 RGB-3-3-2 */
#define IPU_PIX_FMT_RGB555 fourcc('R', 'G', 'B', 'O') /*!< 16 RGB-5-5-5 */
#define IPU_PIX_FMT_RGB565 fourcc('R', 'G', 'B', 'P') /*!< 1 6 RGB-5-6-5 */
#define IPU_PIX_FMT_BGRA4444 fourcc('4', '4', '4', '4') /*!< 16 RGBA-4-4-4-4 */
#define IPU_PIX_FMT_BGRA5551 fourcc('5', '5', '5', '1') /*!< 16 RGBA-5-5-5-1 */
#define IPU_PIX_FMT_RGB666 fourcc('R', 'G', 'B', '6') /*!< 18 RGB-6-6-6 */
#define IPU_PIX_FMT_BGR666 fourcc('B', 'G', 'R', '6') /*!< 18 BGR-6-6-6 */
#define IPU_PIX_FMT_BGR24 fourcc('B', 'G', 'R', '3') /*!< 24 BGR-8-8-8 */
#define IPU_PIX_FMT_RGB24 fourcc('R', 'G', 'B', '3') /*!< 24 RGB-8-8-8 */
#define IPU_PIX_FMT_GBR24 fourcc('G', 'B', 'R', '3') /*!< 24 GBR-8-8-8 */
#define IPU_PIX_FMT_BGR32 fourcc('B', 'G', 'R', '4') /*!< 32 BGR-8-8-8-8 */
#define IPU_PIX_FMT_BGRA32 fourcc('B', 'G', 'R', 'A') /*!< 32 BGR-8-8-8-8 */
#define IPU_PIX_FMT_RGB32 fourcc('R', 'G', 'B', '4') /*!< 32 RGB-8-8-8-8 */
#define IPU_PIX_FMT_RGBA32 fourcc('R', 'G', 'B', 'A') /*!< 32 RGB-8-8-8-8 */
#define IPU_PIX_FMT_ABGR32 fourcc('A', 'B', 'G', 'R') /*!< 32 ABGR-8-8-8-8 */
/*! @} */
/*! @name YUV Interleaved Formats */
/*! @{ */
#define IPU_PIX_FMT_YUYV fourcc('Y', 'U', 'Y', 'V') /*!< 16 YUV 4:2:2 */
#define IPU_PIX_FMT_UYVY fourcc('U', 'Y', 'V', 'Y') /*!< 16 YUV 4:2:2 */
#define IPU_PIX_FMT_YVYU fourcc('Y', 'V', 'Y', 'U') /*!< 16 YVYU 4:2:2 */
#define IPU_PIX_FMT_VYUY fourcc('V', 'Y', 'U', 'Y') /*!< 16 VYYU 4:2:2 */
#define IPU_PIX_FMT_Y41P fourcc('Y', '4', '1', 'P') /*!< 12 YUV 4:1:1 */
#define IPU_PIX_FMT_YUV444 fourcc('Y', '4', '4', '4') /*!< 24 YUV 4:4:4 */
#define IPU_PIX_FMT_VYU444 fourcc('V', '4', '4', '4') /*!< 24 VYU 4:4:4 */
#define IPU_PIX_FMT_AYUV fourcc('A', 'Y', 'U', 'V') /*!< 32 AYUV 4:4:4:4 */
/* two planes -- one Y, one Cb + Cr interleaved */
#define IPU_PIX_FMT_NV12 fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */
#define PRE_PIX_FMT_NV21 fourcc('N', 'V', '2', '1') /* 12 Y/CbCr 4:2:0 */
#define IPU_PIX_FMT_NV16 fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */
#define PRE_PIX_FMT_NV61 fourcc('N', 'V', '6', '1') /* 16 Y/CbCr 4:2:2 */
/* two planes -- 12 tiled Y/CbCr 4:2:0 */
#define IPU_PIX_FMT_TILED_NV12 fourcc('T', 'N', 'V', 'P')
#define IPU_PIX_FMT_TILED_NV12F fourcc('T', 'N', 'V', 'F')
/*! @} */
/*! @name YUV Planar Formats */
/*! @{ */
#define IPU_PIX_FMT_GREY fourcc('G', 'R', 'E', 'Y') /*!< 8 Greyscale */
#define IPU_PIX_FMT_YVU410P fourcc('Y', 'V', 'U', '9') /*!< 9 YVU 4:1:0 */
#define IPU_PIX_FMT_YUV410P fourcc('Y', 'U', 'V', '9') /*!< 9 YUV 4:1:0 */
#define IPU_PIX_FMT_YVU420P fourcc('Y', 'V', '1', '2') /*!< 12 YVU 4:2:0 */
#define IPU_PIX_FMT_YUV420P fourcc('I', '4', '2', '0') /*!< 12 YUV 4:2:0 */
#define IPU_PIX_FMT_YUV420P2 fourcc('Y', 'U', '1', '2') /*!< 12 YUV 4:2:0 */
#define IPU_PIX_FMT_YVU422P fourcc('Y', 'V', '1', '6') /*!< 16 YVU 4:2:2 */
#define IPU_PIX_FMT_YUV422P fourcc('4', '2', '2', 'P') /*!< 16 YUV 4:2:2 */
/* non-interleaved 4:4:4 */
#define IPU_PIX_FMT_YUV444P fourcc('4', '4', '4', 'P') /*!< 24 YUV 4:4:4 */
/*! @} */
#define IPU_PIX_FMT_TILED_NV12_MBALIGN (16)
#define TILED_NV12_FRAME_SIZE(w, h) \
(ALIGN((w) * (h), SZ_4K) + ALIGN((w) * (h) / 2, SZ_4K))
/* IPU device */
typedef enum {
RGB_CS,
YUV_CS,
NULL_CS
} cs_t;
struct ipu_pos {
u32 x;
u32 y;
};
struct ipu_crop {
struct ipu_pos pos;
u32 w;
u32 h;
};
struct ipu_deinterlace {
bool enable;
u8 motion; /*see ipu_motion_sel*/
#define IPU_DEINTERLACE_FIELD_TOP 0
#define IPU_DEINTERLACE_FIELD_BOTTOM 1
#define IPU_DEINTERLACE_FIELD_MASK \
(IPU_DEINTERLACE_FIELD_TOP | IPU_DEINTERLACE_FIELD_BOTTOM)
/* deinterlace frame rate double flags */
#define IPU_DEINTERLACE_RATE_EN 0x80
#define IPU_DEINTERLACE_RATE_FRAME1 0x40
#define IPU_DEINTERLACE_RATE_MASK \
(IPU_DEINTERLACE_RATE_EN | IPU_DEINTERLACE_RATE_FRAME1)
#define IPU_DEINTERLACE_MAX_FRAME 2
u8 field_fmt;
};
struct ipu_input {
u32 width;
u32 height;
u32 format;
struct ipu_crop crop;
dma_addr_t paddr;
struct ipu_deinterlace deinterlace;
dma_addr_t paddr_n; /*valid when deinterlace enable*/
};
struct ipu_alpha {
#define IPU_ALPHA_MODE_GLOBAL 0
#define IPU_ALPHA_MODE_LOCAL 1
u8 mode;
u8 gvalue; /* 0~255 */
dma_addr_t loc_alp_paddr;
};
struct ipu_colorkey {
bool enable;
u32 value; /* RGB 24bit */
};
struct ipu_overlay {
u32 width;
u32 height;
u32 format;
struct ipu_crop crop;
struct ipu_alpha alpha;
struct ipu_colorkey colorkey;
dma_addr_t paddr;
};
struct ipu_output {
u32 width;
u32 height;
u32 format;
u8 rotate;
struct ipu_crop crop;
dma_addr_t paddr;
};
struct ipu_task {
struct ipu_input input;
struct ipu_output output;
bool overlay_en;
struct ipu_overlay overlay;
#define IPU_TASK_PRIORITY_NORMAL 0
#define IPU_TASK_PRIORITY_HIGH 1
u8 priority;
#define IPU_TASK_ID_ANY 0
#define IPU_TASK_ID_VF 1
#define IPU_TASK_ID_PP 2
#define IPU_TASK_ID_MAX 3
u8 task_id;
int timeout;
};
enum {
IPU_CHECK_OK = 0,
IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN = 0x1,
IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN = 0x2,
IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN = 0x4,
IPU_CHECK_ERR_MIN,
IPU_CHECK_ERR_INPUT_CROP,
IPU_CHECK_ERR_OUTPUT_CROP,
IPU_CHECK_ERR_OVERLAY_CROP,
IPU_CHECK_ERR_INPUT_OVER_LIMIT,
IPU_CHECK_ERR_OV_OUT_NO_FIT,
IPU_CHECK_ERR_OVERLAY_WITH_VDI,
IPU_CHECK_ERR_PROC_NO_NEED,
IPU_CHECK_ERR_SPLIT_INPUTW_OVER,
IPU_CHECK_ERR_SPLIT_INPUTH_OVER,
IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER,
IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER,
IPU_CHECK_ERR_SPLIT_WITH_ROT,
IPU_CHECK_ERR_NOT_SUPPORT,
IPU_CHECK_ERR_NOT16ALIGN,
IPU_CHECK_ERR_W_DOWNSIZE_OVER,
IPU_CHECK_ERR_H_DOWNSIZE_OVER,
};
/* IOCTL commands */
#define IPU_CHECK_TASK _IOWR('I', 0x1, struct ipu_task)
#define IPU_QUEUE_TASK _IOW('I', 0x2, struct ipu_task)
#define IPU_ALLOC _IOWR('I', 0x3, int)
#define IPU_FREE _IOW('I', 0x4, int)
#endif

View File

@ -0,0 +1,105 @@
/*
* Copyright 2009-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
*/
/*!
* @file mxc_edid.h
*
* @brief MXC EDID tools
*
* @ingroup Framebuffer
*/
#ifndef MXC_EDID_H
#define MXC_EDID_H
#include <linux/fb.h>
#define FB_VMODE_ASPECT_4_3 0x10
#define FB_VMODE_ASPECT_16_9 0x20
#define FB_VMODE_ASPECT_MASK (FB_VMODE_ASPECT_4_3 | FB_VMODE_ASPECT_16_9)
enum cea_audio_coding_types {
AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0,
AUDIO_CODING_TYPE_LPCM = 1,
AUDIO_CODING_TYPE_AC3 = 2,
AUDIO_CODING_TYPE_MPEG1 = 3,
AUDIO_CODING_TYPE_MP3 = 4,
AUDIO_CODING_TYPE_MPEG2 = 5,
AUDIO_CODING_TYPE_AACLC = 6,
AUDIO_CODING_TYPE_DTS = 7,
AUDIO_CODING_TYPE_ATRAC = 8,
AUDIO_CODING_TYPE_SACD = 9,
AUDIO_CODING_TYPE_EAC3 = 10,
AUDIO_CODING_TYPE_DTS_HD = 11,
AUDIO_CODING_TYPE_MLP = 12,
AUDIO_CODING_TYPE_DST = 13,
AUDIO_CODING_TYPE_WMAPRO = 14,
AUDIO_CODING_TYPE_RESERVED = 15,
};
struct mxc_hdmi_3d_format {
unsigned char vic_order_2d;
unsigned char struct_3d;
unsigned char detail_3d;
unsigned char reserved;
};
struct mxc_edid_cfg {
bool cea_underscan;
bool cea_basicaudio;
bool cea_ycbcr444;
bool cea_ycbcr422;
bool hdmi_cap;
/*VSD*/
bool vsd_support_ai;
bool vsd_dc_48bit;
bool vsd_dc_36bit;
bool vsd_dc_30bit;
bool vsd_dc_y444;
bool vsd_dvi_dual;
bool vsd_cnc0;
bool vsd_cnc1;
bool vsd_cnc2;
bool vsd_cnc3;
u8 vsd_video_latency;
u8 vsd_audio_latency;
u8 vsd_I_video_latency;
u8 vsd_I_audio_latency;
u8 physical_address[4];
u8 hdmi_vic[64];
struct mxc_hdmi_3d_format hdmi_3d_format[64];
u16 hdmi_3d_mask_all;
u16 hdmi_3d_struct_all;
u32 vsd_max_tmdsclk_rate;
u8 max_channels;
u8 sample_sizes;
u8 sample_rates;
u8 speaker_alloc;
};
int mxc_edid_var_to_vic(struct fb_var_screeninfo *var);
int mxc_edid_mode_to_vic(const struct fb_videomode *mode);
int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi);
int mxc_edid_parse_ext_blk(unsigned char *edid, struct mxc_edid_cfg *cfg,
struct fb_monspecs *specs);
#endif

File diff suppressed because it is too large Load Diff