9
0
Fork 0

Merge branch 'for-next/imx-ipu-v3'

This commit is contained in:
Sascha Hauer 2014-04-04 09:57:30 +02:00
commit 74b5a3dbb3
77 changed files with 9399 additions and 272 deletions

View File

@ -100,3 +100,4 @@ obj-$(CONFIG_MACH_USB_A9G20) += usb-a926x/
obj-$(CONFIG_MACH_VERSATILEPB) += versatile/
obj-$(CONFIG_MACH_VEXPRESS) += vexpress/
obj-$(CONFIG_MACH_ZEDBOARD) += avnet-zedboard/
obj-$(CONFIG_MACH_VARISCITE_MX6) += variscite-mx6/

View File

@ -75,7 +75,6 @@ static struct fb_videomode falconwing_vmode = {
.lower_margin = 8,
.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
};
#define MAX_FB_SIZE SZ_1M

View File

@ -76,7 +76,6 @@ static struct fb_videomode imxfb_mode = {
.vsync_len = 3,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
};
static void eukrea_cpuimx35_enable_display(int enable)

View File

@ -62,7 +62,6 @@ static struct imx_fb_videomode imx_fb_modedata = {
.vsync_len = 1,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
},
.pcr = 0xfb108bc7,
.bpp = 16,

View File

@ -201,7 +201,6 @@ static struct fb_videomode mx28_evk_vmodes[] = {
.lower_margin = 10,
.sync = FB_SYNC_DE_HIGH_ACT | FB_SYNC_CLK_INVERT,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
}
};

View File

@ -95,7 +95,6 @@ static struct fb_videomode CTP_CLAA070LC0ACW = {
.vsync_len = 1, /* note: DE only display */
.sync = FB_SYNC_CLK_IDLE_EN | FB_SYNC_OE_ACT_HIGH,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
};
static struct imx_ipu_fb_platform_data ipu_fb_data = {

View File

@ -85,7 +85,6 @@ static struct fb_videomode s3c24x0_fb_modes[] = {
.pixclock = 115913,
.sync = FB_SYNC_USE_PWREN,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
},
#endif
#ifdef CONFIG_MINI2440_VIDEO_A70

View File

@ -68,7 +68,6 @@ static struct fb_videomode guf_cupid_fb_mode = {
.sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_CLK_INVERT |
FB_SYNC_OE_ACT_HIGH,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
};
#define GPIO_LCD_ENABLE (2 * 32 + 24)

View File

@ -72,7 +72,6 @@ static struct fb_videomode tx28evk_vmodes[] = {
.lower_margin = 10,
.sync = FB_SYNC_DE_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
}, {
/*
* Emerging ETV570 640 x 480 display (directly connected)
@ -93,7 +92,6 @@ static struct fb_videomode tx28evk_vmodes[] = {
.lower_margin = 10,
.sync = FB_SYNC_DE_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
/*
* This display is connected:
* display side -> CPU side
@ -127,7 +125,6 @@ static struct fb_videomode tx28evk_vmodes[] = {
.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT |
FB_SYNC_DE_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
}, {
/*
* Modeline "1024x768" x 60.0
@ -149,7 +146,6 @@ static struct fb_videomode tx28evk_vmodes[] = {
.lower_margin = 3,
.sync = FB_SYNC_DE_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
}, {
/*
* Modeline "1280x1024" x 60.0
@ -172,7 +168,6 @@ static struct fb_videomode tx28evk_vmodes[] = {
.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT |
FB_SYNC_DE_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
},
};

View File

@ -73,7 +73,6 @@ static struct fb_videomode pcm043_fb_mode[] = {
.vsync_len = 1,
.sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
}, {
/* 240x320 @ 60 Hz */
.name = "Sharp-LQ035Q7",
@ -90,7 +89,6 @@ static struct fb_videomode pcm043_fb_mode[] = {
.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | \
FB_SYNC_CLK_INVERT | FB_SYNC_CLK_IDLE_EN,
.vmode = FB_VMODE_NONINTERLACED,
.flag = 0,
}
};

View File

@ -0,0 +1,3 @@
obj-y += board.o flash-header-variscite.dcd.o
extra-y += flash-header-variscite.dcd.S flash-header-variscite.dcd
lwl-y += lowlevel.o

View File

@ -0,0 +1,135 @@
/*
* Copyright (C) 2013 Michael Burkey
* Based on code (C) Sascha Hauer, Pengutronix
* Based on code (C) Variscite, Ltd.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation.
*
*/
#define pr_fmt(fmt) "var-som-mx6: " fmt
#include <common.h>
#include <gpio.h>
#include <init.h>
#include <of.h>
#include <debug_ll.h>
#include <environment.h>
#include <asm/armlinux.h>
#include <generated/mach-types.h>
#include <partition.h>
#include <asm/io.h>
#include <asm/mmu.h>
#include <mach/generic.h>
#include <sizes.h>
#include <mach/imx6.h>
#include <mach/devices-imx6.h>
#include <mach/iomux-mx6.h>
#include <spi/spi.h>
#include <mach/spi.h>
#include <i2c/i2c.h>
#define ETH_PHY_RST IMX_GPIO_NR(1, 25)
static int setup_pmic_voltages(void)
{
unsigned char value, rev_id = 0 ;
struct i2c_adapter *adapter = NULL;
struct i2c_client client;
int addr = -1, ret, bus = 0;
/* I2C2 bus (2-1 = 1 in barebox numbering) */
bus = 1;
/* PFUZE100 device address is 0x08 */
addr = 0x08;
adapter = i2c_get_adapter(bus);
if (!adapter) {
pr_err("i2c bus %d not found\n", bus);
return -ENODEV;
}
client.adapter = adapter;
client.addr = addr;
/* Attempt to locate the PFUZE100 chip. */
if (i2c_read_reg(&client, 0x00, &value, 1) != 1) {
pr_err("Read device ID error!\n");
return -1;
}
if (i2c_read_reg(&client, 0x03, &rev_id, 1) != 1) {
pr_err("Read Rev ID error!\n");
return -1;
}
pr_info("Found PFUZE100! deviceid=%x,revid=%x\n", value, rev_id);
/* Set Gigabit Ethernet voltage (SOM v1.1/1.0)*/
value = 0x60;
ret = i2c_write_reg(&client, 0x4a, &value, 1);
/* set VGEN3 to 2.5V */
value = 0x77;
if (i2c_write_reg(&client, 0x6e, &value, 1) != 1) {
pr_err("Set VGEN3 error!\n");
return -EIO;
}
return 0;
}
static int eth_phy_reset(void)
{
gpio_request(ETH_PHY_RST, "phy reset");
gpio_direction_output(ETH_PHY_RST, 0);
mdelay(1);
gpio_set_value(ETH_PHY_RST, 1);
return 0;
}
static int variscite_custom_init(void)
{
if (!of_machine_is_compatible("variscite,imx6q-custom"))
return 0;
barebox_set_hostname("var-som-mx6");
setup_pmic_voltages();
eth_phy_reset();
armlinux_set_architecture(MACH_TYPE_VAR_SOM_MX6);
pr_debug("Completing custom_init()\n");
return 0;
}
device_initcall(variscite_custom_init);
static int variscite_custom_core_init(void)
{
if (!of_machine_is_compatible("variscite,imx6q-custom"))
return 0;
imx6_init_lowlevel();
pr_debug("Completing custom_core_init()\n");
return 0;
}
postcore_initcall(variscite_custom_core_init);

View File

@ -0,0 +1,86 @@
loadaddr 0x10000000
soc imx6
dcdofs 0x400
wm 32 0x020e0798 0x000C0000
wm 32 0x020e0758 0x00000000
wm 32 0x020e0588 0x00000030
wm 32 0x020e0594 0x00000030
wm 32 0x020e056c 0x00000030
wm 32 0x020e0578 0x00000030
wm 32 0x020e074c 0x00000030
wm 32 0x020e057c 0x00000030
wm 32 0x020e058c 0x00000000
wm 32 0x020e059c 0x00000030
wm 32 0x020e05a0 0x00000030
wm 32 0x020e078c 0x00000030
wm 32 0x020e0750 0x00020000
wm 32 0x020e05a8 0x00000030
wm 32 0x020e05b0 0x00000030
wm 32 0x020e0524 0x00000030
wm 32 0x020e051c 0x00000030
wm 32 0x020e0518 0x00000030
wm 32 0x020e050c 0x00000030
wm 32 0x020e05b8 0x00000030
wm 32 0x020e05c0 0x00000030
wm 32 0x020e0774 0x00020000
wm 32 0x020e0784 0x00000030
wm 32 0x020e0788 0x00000030
wm 32 0x020e0794 0x00000030
wm 32 0x020e079c 0x00000030
wm 32 0x020e07a0 0x00000030
wm 32 0x020e07a4 0x00000030
wm 32 0x020e07a8 0x00000030
wm 32 0x020e0748 0x00000030
wm 32 0x020e05ac 0x00000030
wm 32 0x020e05b4 0x00000030
wm 32 0x020e0528 0x00000030
wm 32 0x020e0520 0x00000030
wm 32 0x020e0514 0x00000030
wm 32 0x020e0510 0x00000030
wm 32 0x020e05bc 0x00000030
wm 32 0x020e05c4 0x00000030
wm 32 0x021b0800 0xA1390003
wm 32 0x021b080c 0x001F001F
wm 32 0x021b0810 0x001F001F
wm 32 0x021b480c 0x001F001F
wm 32 0x021b4810 0x001F001F
wm 32 0x021b083c 0x4333033F
wm 32 0x021b0840 0x032C031D
wm 32 0x021b483c 0x43200332
wm 32 0x021b4840 0x031A026A
wm 32 0x021b0848 0x4D464746
wm 32 0x021b4848 0x47453F4D
wm 32 0x021b0850 0x3E434440
wm 32 0x021b4850 0x47384839
wm 32 0x021b081c 0x33333333
wm 32 0x021b0820 0x33333333
wm 32 0x021b0824 0x33333333
wm 32 0x021b0828 0x33333333
wm 32 0x021b481c 0x33333333
wm 32 0x021b4820 0x33333333
wm 32 0x021b4824 0x33333333
wm 32 0x021b4828 0x33333333
wm 32 0x021b08b8 0x00000800
wm 32 0x021b48b8 0x00000800
wm 32 0x021b0004 0x00020036
wm 32 0x021b0008 0x09444040
wm 32 0x021b000c 0x555A7975
wm 32 0x021b0010 0xFF538F64
wm 32 0x021b0014 0x01FF00DB
wm 32 0x021b0018 0x00001740
wm 32 0x021b001c 0x00008000
wm 32 0x021b002c 0x000026D2
wm 32 0x021b0030 0x005A1023
wm 32 0x021b0040 0x00000027
wm 32 0x021b0000 0x831A0000
wm 32 0x021b001c 0x04088032
wm 32 0x021b001c 0x00008033
wm 32 0x021b001c 0x00048031
wm 32 0x021b001c 0x09408030
wm 32 0x021b001c 0x04008040
wm 32 0x021b0020 0x00005800
wm 32 0x021b0818 0x00011117
wm 32 0x021b4818 0x00011117
wm 32 0x021b0004 0x00025576
wm 32 0x021b0404 0x00011006
wm 32 0x021b001c 0x00000000

View File

@ -0,0 +1,74 @@
/*
*
* Copyright (C) 2013 Michael Burkey
* Based on code by Sascha Hauer <s.hauer@pengutronix.de>
*
* 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.
*
*/
#include <debug_ll.h>
#include <common.h>
#include <sizes.h>
#include <io.h>
#include <asm/barebox-arm-head.h>
#include <asm/barebox-arm.h>
#include <asm/sections.h>
#include <asm/cache.h>
#include <asm/mmu.h>
#include <mach/imx6-mmdc.h>
#include <mach/imx6.h>
static inline void setup_uart(void)
{
void __iomem *ccmbase = (void *)MX6_CCM_BASE_ADDR;
void __iomem *uartbase = (void *)MX6_UART1_BASE_ADDR;
void __iomem *iomuxbase = (void *)MX6_IOMUXC_BASE_ADDR;
writel(0x03, iomuxbase + 0x0280);
writel(0x03, iomuxbase + 0x0284);
writel(0xffffffff, ccmbase + 0x68);
writel(0xffffffff, ccmbase + 0x6c);
writel(0xffffffff, ccmbase + 0x70);
writel(0xffffffff, ccmbase + 0x74);
writel(0xffffffff, ccmbase + 0x78);
writel(0xffffffff, ccmbase + 0x7c);
writel(0xffffffff, ccmbase + 0x80);
writel(0x00000000, uartbase + 0x80);
writel(0x00004027, uartbase + 0x84);
writel(0x00000704, uartbase + 0x88);
writel(0x00000a81, uartbase + 0x90);
writel(0x0000002b, uartbase + 0x9c);
writel(0x00013880, uartbase + 0xb0);
writel(0x0000047f, uartbase + 0xa4);
writel(0x0000c34f, uartbase + 0xa8);
writel(0x00000001, uartbase + 0x80);
putc_ll('>');
}
extern char __dtb_imx6q_var_custom_start[];
ENTRY_FUNCTION(start_variscite_custom, r0, r1, r2)
{
uint32_t fdt;
arm_cpu_lowlevel_init();
arm_setup_stack(0x00920000 - 8);
if (IS_ENABLED(CONFIG_DEBUG_LL))
setup_uart();
fdt = (uint32_t)__dtb_imx6q_var_custom_start - get_runtime_offset();
barebox_arm_entry(0x10000000, SZ_1G, fdt);
}

View File

@ -12,9 +12,11 @@ CONFIG_MACH_REALQ7=y
CONFIG_MACH_GK802=y
CONFIG_MACH_TQMA6X=y
CONFIG_MACH_SABRELITE=y
CONFIG_MACH_SABRESD=y
CONFIG_MACH_NITROGEN6X=y
CONFIG_MACH_SOLIDRUN_HUMMINGBOARD=y
CONFIG_MACH_UDOO=y
CONFIG_MACH_VARISCITE_MX6=y
CONFIG_IMX_IIM=y
CONFIG_IMX_IIM_FUSE_BLOW=y
CONFIG_IMX_OCOTP=y
@ -41,9 +43,11 @@ CONFIG_CMD_EDIT=y
CONFIG_CMD_SLEEP=y
CONFIG_CMD_MSLEEP=y
CONFIG_CMD_SAVEENV=y
CONFIG_CMD_LOADENV=y
CONFIG_CMD_EXPORT=y
CONFIG_CMD_PRINTENV=y
CONFIG_CMD_READLINE=y
CONFIG_CMD_READF=y
CONFIG_CMD_LET=y
CONFIG_CMD_MENU=y
CONFIG_CMD_MENU_MANAGEMENT=y
@ -72,6 +76,7 @@ CONFIG_CMD_OFTREE=y
CONFIG_CMD_OF_PROPERTY=y
CONFIG_CMD_OF_NODE=y
CONFIG_CMD_MEMTEST=y
CONFIG_CMD_SPLASH=y
CONFIG_CMD_BAREBOX_UPDATE=y
CONFIG_CMD_TIMEOUT=y
CONFIG_CMD_PARTITION=y
@ -94,8 +99,8 @@ CONFIG_NET_NETCONSOLE=y
CONFIG_NET_RESOLV=y
CONFIG_OFDEVICE=y
CONFIG_OF_BAREBOX_DRIVERS=y
CONFIG_AT803X_PHY=y
CONFIG_DRIVER_NET_FEC_IMX=y
CONFIG_AT803X_PHY=y
CONFIG_NET_USB=y
CONFIG_NET_USB_ASIX=y
CONFIG_NET_USB_SMSC95XX=y
@ -107,6 +112,13 @@ CONFIG_MTD_RAW_DEVICE=y
CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_M25P80=y
CONFIG_MTD_SST25L=y
CONFIG_NAND=y
CONFIG_NAND_ALLOW_ERASE_BAD=y
CONFIG_NAND_IMX=y
CONFIG_NAND_IMX_BBM=y
CONFIG_NAND_MXS=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_FASTMAP=y
CONFIG_DISK_AHCI=y
CONFIG_DISK_AHCI_IMX=y
CONFIG_DISK_INTF_PLATFORM_IDE=y
@ -118,6 +130,12 @@ CONFIG_USB_ULPI=y
CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DFU=y
CONFIG_VIDEO=y
CONFIG_DRIVER_VIDEO_IMX_IPUV3=y
CONFIG_DRIVER_VIDEO_IMX_IPUV3_LVDS=y
CONFIG_DRIVER_VIDEO_IMX_IPUV3_HDMI=y
CONFIG_DRIVER_VIDEO_SIMPLEFB=y
CONFIG_DRIVER_VIDEO_EDID=y
CONFIG_MCI=y
CONFIG_MCI_MMC_BOOT_PARTITIONS=y
CONFIG_MCI_IMX_ESDHC=y
@ -133,6 +151,9 @@ CONFIG_EEPROM_AT24=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_IMX=y
CONFIG_PWM=y
CONFIG_PWM_IMX=y
CONFIG_MXS_APBH_DMA=y
CONFIG_GPIO_STMPE=y
CONFIG_FS_EXT4=y
CONFIG_FS_TFTP=y
@ -140,4 +161,5 @@ CONFIG_FS_NFS=y
CONFIG_FS_FAT=y
CONFIG_FS_FAT_WRITE=y
CONFIG_FS_FAT_LFN=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_FS_UBIFS=y
CONFIG_FS_UBIFS_COMPRESSION_LZO=y

View File

@ -25,7 +25,8 @@ dtb-$(CONFIG_ARCH_IMX6) += imx6q-gk802.dtb \
imx6q-guf-santaro.dtb \
imx6q-nitrogen6x.dtb \
imx6dl-nitrogen6x.dtb \
imx6q-udoo.dtb
imx6q-udoo.dtb \
imx6q-var-custom.dtb
dtb-$(CONFIG_ARCH_MVEBU) += dove-cubox-bb.dtb
dtb-$(CONFIG_ARCH_SOCFPGA) += socfpga_cyclone5_sockit.dtb \
socfpga_cyclone5_socrates.dtb
@ -60,6 +61,7 @@ pbl-$(CONFIG_MACH_SABRESD) += imx6q-sabresd.dtb.o
pbl-$(CONFIG_MACH_GUF_SANTARO) += imx6q-guf-santaro.dtb.o
pbl-$(CONFIG_MACH_NITROGEN6X) += imx6q-nitrogen6x.dtb.o imx6dl-nitrogen6x.dtb.o
pbl-$(CONFIG_MACH_UDOO) += imx6q-udoo.dtb.o
pbl-$(CONFIG_MACH_VARISCITE_MX6) += imx6q-var-custom.dtb.o
.SECONDARY: $(obj)/$(BUILTIN_DTB).dtb.S
.SECONDARY: $(patsubst %,$(obj)/%.S,$(dtb-y))

View File

@ -87,7 +87,7 @@
ipu: ipu@18000000 {
#crtc-cells = <1>;
compatible = "fsl,imx53-ipu";
reg = <0x18000000 0x080000000>;
reg = <0x18000000 0x08000000>;
interrupts = <11 10>;
clocks = <&clks 59>, <&clks 110>, <&clks 61>;
clock-names = "bus", "di0", "di1";

View File

@ -0,0 +1,149 @@
/*
* Copyright 2013 Michael Burkey
* Based on code written by Christian Hemp, Phytec Messtechnik GmbH
*
* 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
*
*
* environment@0 {
compatible = "barebox,environment";
device-path = &usdhc2, "partname:barebox-environment";
};
*
*/
/dts-v1/;
#include "imx6q-var-som.dtsi"
/ {
model = "Variscite i.MX6 Quad Custom Carrier-Board";
compatible = "variscite,imx6q-custom", "variscite,imx6q-som", "fsl,imx6q";
chosen {
linux,stdout-path = &uart1;
environment@0 {
compatible = "barebox,environment";
device-path = &gpmi, "partname:barebox-environment";
};
};
};
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
imx6q-variscite-custom {
pinctrl_i2c1: i2c1grp {
fsl,pins = <MX6QDL_I2C1_PINGRP1>;
};
pinctrl_i2c3: i2c3grp {
fsl,pins = <MX6QDL_I2C3_PINGRP3>;
};
pinctrl_usdhc2: usdhc2grp {
fsl,pins = <MX6QDL_USDHC2_PINGRP_D4>;
};
pinctrl_usdhc2_cd: usdhc2cd {
fsl,pins = <
MX6QDL_PAD_KEY_COL4__GPIO4_IO14 0x80000000
MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000
>;
};
pinctrl_uart1: uart1grp {
fsl,pins = <MX6QDL_UART1_PINGRP1>;
};
};
};
&ldb {
status = "okay";
lvds-channel@0 {
fsl,data-mapping = "spwg";
fsl,data-width = <24>;
status = "okay";
display-timings {
native-mode = &claawvga;
claawvga: claawvga {
native-mode;
clock-frequency = <35714000>;
hactive = <800>;
vactive = <480>;
hback-porch = <28>;
hfront-porch = <17>;
vback-porch = <13>;
vfront-porch = <20>;
hsync-len = <20>;
vsync-len = <13>;
};
};
};
};
&uart1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
};
&usdhc2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usdhc2>;
cd-gpios = <&gpio4 14 0>;
wp-gpios = <&gpio4 15 0>;
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "barebox";
reg = <0x0 0x200000>;
};
partition@1 {
label = "barebox-environment";
reg = <0x200000 0x20000>;
};
partition@2 {
label = "kernel";
reg = <0x220000 0x600000>;
};
partition@3 {
label = "rootfs";
reg = <0x820000 0x18000000>;
};
};
&usbh1 {
status = "okay";
disable-over-current;
dr_mode = "host";
phy_type = "utmi";
};
&i2c1 {
status = "okay";
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
};
&i2c3 {
status = "okay";
clock-frequency = <1000000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c3>;
};

View File

@ -0,0 +1,99 @@
/*
* Copyright 2013 Michael Burkey
* Based on code written by Christian Hemp, Phytec Messtechnik GmbH
*
* 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 "imx6q.dtsi"
/ {
model = "Variscite i.MX6 Quad SOM";
compatible = "variscite,imx6q-som", "fsl,imx6q";
memory {
reg = <0x10000000 0x40000000>;
};
};
&fec {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet>;
phy-mode = "rgmii";
status = "okay";
};
&gpmi {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpmi_nand>;
nand-on-flash-bbt;
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "barebox";
reg = <0x0 0x200000>;
};
partition@1 {
label = "barebox-environment";
reg = <0x200000 0x20000>;
};
partition@2 {
label = "kernel";
reg = <0x220000 0x600000>;
};
partition@3 {
label = "rootfs";
reg = <0x820000 0x1F7E0000>;
};
};
&hdmi {
status = "okay";
ddc-i2c-bus = <&i2c2>;
};
&i2c2 {
status = "okay";
clock-frequency = <1000000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
};
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
imx6q-variscite-som {
pinctrl_hog: hoggrp {
fsl,pins = <
MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x80000000
MX6QDL_PAD_GPIO_3__CCM_CLKO2 0x80000000
>;
};
pinctrl_enet: enetgrp {
fsl,pins = <
MX6QDL_ENET_PINGRP_RGMII_MD(0x1b0b0, 0x1b0b0)
MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x80000000 /* KSZ9031 PHY Reset */
>;
};
pinctrl_gpmi_nand: gpmigrp {
fsl,pins = <MX6QDL_GPMI_NAND_PINGRP1>;
};
pinctrl_i2c2: i2c2grp {
fsl,pins = <MX6QDL_I2C2_PINGRP2>;
};
};
};

View File

@ -16,6 +16,7 @@
/ {
aliases {
spi4 = &ecspi5;
ipu1 = &ipu2;
};
cpus {
@ -157,6 +158,10 @@
};
};
&hdmi {
compatible = "fsl,imx6q-hdmi";
};
&ldb {
clocks = <&clks 33>, <&clks 34>,
<&clks 39>, <&clks 40>, <&clks 41>, <&clks 42>,

View File

@ -45,6 +45,7 @@
spi3 = &ecspi4;
usbphy0 = &usbphy1;
usbphy1 = &usbphy2;
ipu0 = &ipu1;
};
intc: interrupt-controller@00a01000 {
@ -664,6 +665,17 @@
};
};
hdmi: hdmi@0120000 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x00120000 0x9000>;
interrupts = <0 115 0x04>;
gpr = <&gpr>;
clocks = <&clks 123>, <&clks 124>;
clock-names = "iahb", "isfr";
status = "disabled";
};
dcic1: dcic@020e4000 {
reg = <0x020e4000 0x4000>;
interrupts = <0 124 IRQ_TYPE_LEVEL_HIGH>;

View File

@ -95,9 +95,9 @@ static __init int clps711x_clk_init(void)
clks[timer_hf].clk = clk_fixed(clks[timer_hf].name, f_timer_hf);
clks[timer_lf].clk = clk_fixed(clks[timer_lf].name, f_timer_lf);
clks[tc1].clk = clk_mux(clks[tc1].name, IOMEM(SYSCON1), 5, 1,
tc_sel_clks, ARRAY_SIZE(tc_sel_clks));
tc_sel_clks, ARRAY_SIZE(tc_sel_clks), 0);
clks[tc2].clk = clk_mux(clks[tc2].name, IOMEM(SYSCON1), 7, 1,
tc_sel_clks, ARRAY_SIZE(tc_sel_clks));
tc_sel_clks, ARRAY_SIZE(tc_sel_clks), 0);
clps711x_clk_register(dummy);
clps711x_clk_register(cpu);

View File

@ -39,6 +39,7 @@ config ARCH_TEXT_BASE
default 0x4fc00000 if MACH_PHYTEC_PFLA02
default 0x4fc00000 if MACH_DFI_FS700_M60
default 0x4fc00000 if MACH_UDOO
default 0x4fc00000 if MACH_VARISCITE_MX6
config ARCH_IMX_INTERNAL_BOOT
bool "support internal boot mode"
@ -247,6 +248,11 @@ config MACH_UDOO
bool "Freescale i.MX6 UDOO Board"
select ARCH_IMX6
config MACH_VARISCITE_MX6
bool "Variscite i.MX6 Quad SOM"
select ARCH_IMX6
endif
# ----------------------------------------------------------

View File

@ -19,6 +19,8 @@
#include <linux/clkdev.h>
#include <linux/err.h>
#include <mach/imx6-regs.h>
#include <mach/revision.h>
#include <mach/imx6.h>
#include "clk.h"
@ -84,8 +86,10 @@ enum mx6q_clks {
usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg,
pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg,
ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5,
sata_ref, pcie_ref, sata_ref_100m, pcie_ref_125m, enet_ref,
clk_max
sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate,
usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, eim_slow,
spdif, cko2_sel, cko2_podf, cko2, cko, vdoa, pll4_audio_div,
lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, clk_max
};
static struct clk *clks[clk_max];
@ -171,6 +175,62 @@ static const char *cko1_sels[] = {
"pll4_audio",
};
static const char *ipu_sels[] = {
"mmdc_ch0_axi_podf",
"pll2_pfd2_396m",
"pll3_120m",
"pll3_pfd1_540m",
};
static const char *ldb_di_sels[] = {
"pll5_video_div",
"pll2_pfd0_352m",
"pll2_pfd2_396m",
"mmdc_ch1_axi_podf",
"pll3_usb_otg",
};
static const char *ipu_di_pre_sels[] = {
"mmdc_ch0_axi",
"pll3_usb_otg",
"pll5_video_div",
"pll2_pfd0_352m",
"pll2_pfd2_396m",
"pll3_pfd1_540m",
};
static const char *ipu1_di0_sels[] = {
"ipu1_di0_pre",
"dummy",
"dummy",
"ldb_di0_podf",
"ldb_di1_podf",
};
static const char *ipu1_di1_sels[] = {
"ipu1_di1_pre",
"dummy",
"dummy",
"ldb_di0_podf",
"ldb_di1_podf",
};
static const char *ipu2_di0_sels[] = {
"ipu2_di0_pre",
"dummy",
"dummy",
"ldb_di0_podf",
"ldb_di1_podf",
};
static const char *ipu2_di1_sels[] = {
"ipu2_di1_pre",
"dummy",
"dummy",
"ldb_di0_podf",
"ldb_di1_podf",
};
static struct clk_div_table clk_enet_ref_table[] = {
{ .val = 0, .div = 20, },
{ .val = 1, .div = 10, },
@ -179,6 +239,86 @@ static struct clk_div_table clk_enet_ref_table[] = {
{ },
};
static struct clk_div_table post_div_table[] = {
{ .val = 2, .div = 1, },
{ .val = 1, .div = 2, },
{ .val = 0, .div = 4, },
{ /* sentinel */ }
};
static struct clk_div_table video_div_table[] = {
{ .val = 0, .div = 1, },
{ .val = 1, .div = 2, },
{ .val = 2, .div = 1, },
{ .val = 3, .div = 4, },
{ /* sentinel */ }
};
static void imx6_add_video_clks(void __iomem *anab, void __iomem *cb)
{
clks[pll5_post_div] = imx_clk_divider_table("pll5_post_div", "pll5_video", anab + 0xa0, 19, 2, post_div_table);
clks[pll5_video_div] = imx_clk_divider_table("pll5_video_div", "pll5_post_div", anab + 0x170, 30, 2, video_div_table);
clks[ipu1_sel] = imx_clk_mux("ipu1_sel", cb + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clks[ipu2_sel] = imx_clk_mux("ipu2_sel", cb + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clks[ldb_di0_sel] = imx_clk_mux_p("ldb_di0_sel", cb + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
clks[ldb_di1_sel] = imx_clk_mux_p("ldb_di1_sel", cb + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
clks[ipu1_di0_pre_sel] = imx_clk_mux_p("ipu1_di0_pre_sel", cb + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
clks[ipu1_di1_pre_sel] = imx_clk_mux_p("ipu1_di1_pre_sel", cb + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
clks[ipu2_di0_pre_sel] = imx_clk_mux_p("ipu2_di0_pre_sel", cb + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
clks[ipu2_di1_pre_sel] = imx_clk_mux_p("ipu2_di1_pre_sel", cb + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
clks[ipu1_di0_sel] = imx_clk_mux_p("ipu1_di0_sel", cb + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels));
clks[ipu1_di1_sel] = imx_clk_mux_p("ipu1_di1_sel", cb + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels));
clks[ipu2_di0_sel] = imx_clk_mux_p("ipu2_di0_sel", cb + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels));
clks[ipu2_di1_sel] = imx_clk_mux_p("ipu2_di1_sel", cb + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels));
clks[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", cb + 0x3c, 11, 3);
clks[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", cb + 0x3c, 16, 3);
clks[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
clks[ldb_di0_podf] = imx_clk_divider("ldb_di0_podf", "ldb_di0_div_3_5", cb + 0x20, 10, 1);
clks[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
clks[ldb_di1_podf] = imx_clk_divider("ldb_di1_podf", "ldb_di1_div_3_5", cb + 0x20, 11, 1);
clks[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", cb + 0x34, 3, 3);
clks[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", cb + 0x34, 12, 3);
clks[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", cb + 0x38, 3, 3);
clks[ipu2_di1_pre] = imx_clk_divider("ipu2_di1_pre", "ipu2_di1_pre_sel", cb + 0x38, 12, 3);
clkdev_add_physbase(clks[ipu1_podf], MX6_IPU1_BASE_ADDR, "bus");
clkdev_add_physbase(clks[ipu1_di0_sel], MX6_IPU1_BASE_ADDR, "di0");
clkdev_add_physbase(clks[ipu1_di1_sel], MX6_IPU1_BASE_ADDR, "di1");
clkdev_add_physbase(clks[ipu2_podf], MX6_IPU2_BASE_ADDR, "bus");
clkdev_add_physbase(clks[ipu2_di0_sel], MX6_IPU2_BASE_ADDR, "di0");
clkdev_add_physbase(clks[ipu2_di1_sel], MX6_IPU2_BASE_ADDR, "di1");
clkdev_add_physbase(clks[ldb_di0_sel], 0x020e0008, "di0_pll");
clkdev_add_physbase(clks[ldb_di1_sel], 0x020e0008, "di1_pll");
clkdev_add_physbase(clks[ipu1_di0_sel], 0x020e0008, "di0_sel");
clkdev_add_physbase(clks[ipu1_di1_sel], 0x020e0008, "di1_sel");
clkdev_add_physbase(clks[ipu2_di0_sel], 0x020e0008, "di2_sel");
clkdev_add_physbase(clks[ipu2_di1_sel], 0x020e0008, "di3_sel");
clkdev_add_physbase(clks[ldb_di0], 0x020e0008, "di0");
clkdev_add_physbase(clks[ldb_di1], 0x020e0008, "di1");
clkdev_add_physbase(clks[ahb], 0x00120000, "iahb");
clkdev_add_physbase(clks[pll3_pfd1_540m], 0x00120000, "isfr");
clk_set_parent(clks[ipu1_di0_sel], clks[ipu1_di0_pre]);
clk_set_parent(clks[ipu1_di1_sel], clks[ipu1_di1_pre]);
clk_set_parent(clks[ipu2_di0_sel], clks[ipu2_di0_pre]);
clk_set_parent(clks[ipu2_di1_sel], clks[ipu2_di1_pre]);
clk_set_parent(clks[ipu1_di0_pre_sel], clks[pll5_video_div]);
clk_set_parent(clks[ipu1_di1_pre_sel], clks[pll5_video_div]);
clk_set_parent(clks[ipu2_di0_pre_sel], clks[pll5_video_div]);
clk_set_parent(clks[ipu2_di1_pre_sel], clks[pll5_video_div]);
if ((imx_silicon_revision() != IMX_CHIP_REV_1_0) ||
cpu_is_mx6dl()) {
clk_set_parent(clks[ldb_di0_sel], clks[pll5_video_div]);
clk_set_parent(clks[ldb_di1_sel], clks[pll5_video_div]);
}
}
static int imx6_ccm_probe(struct device_d *dev)
{
void __iomem *base, *anatop_base, *ccm_base;
@ -214,7 +354,7 @@ static int imx6_ccm_probe(struct device_d *dev)
clks[sata_ref_100m] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20);
clks[pcie_ref_125m] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19);
clks[enet_ref] = clk_divider_table("enet_ref", "pll6_enet", base + 0xe0, 0, 2, clk_enet_ref_table);
clks[enet_ref] = imx_clk_divider_table("enet_ref", "pll6_enet", base + 0xe0, 0, 2, clk_enet_ref_table);
/* name parent_name reg idx */
clks[pll2_pfd0_352m] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0);
@ -281,7 +421,6 @@ static int imx6_ccm_probe(struct device_d *dev)
clks[arm] = imx_clk_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16);
clks[ahb] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1);
clkdev_add_physbase(clks[uart_serial_podf], MX6_UART1_BASE_ADDR, NULL);
clkdev_add_physbase(clks[uart_serial_podf], MX6_UART2_BASE_ADDR, NULL);
clkdev_add_physbase(clks[uart_serial_podf], MX6_UART3_BASE_ADDR, NULL);
@ -310,10 +449,16 @@ static int imx6_ccm_probe(struct device_d *dev)
clkdev_add_physbase(clks[ipg_per], MX6_PWM3_BASE_ADDR, "per");
clkdev_add_physbase(clks[ipg_per], MX6_PWM4_BASE_ADDR, "per");
if (IS_ENABLED(CONFIG_DRIVER_VIDEO_IMX_IPUV3))
imx6_add_video_clks(anatop_base, ccm_base);
writel(0xffffffff, ccm_base + CCGR0);
writel(0xf0ffffff, ccm_base + CCGR1); /* gate GPU3D, GPU2D */
writel(0xffffffff, ccm_base + CCGR2);
writel(0x3fff0000, ccm_base + CCGR3); /* gate OpenVG, LDB, IPU1, IPU2 */
if (IS_ENABLED(CONFIG_DRIVER_VIDEO_IMX_IPUV3))
writel(0xffffffff, ccm_base + CCGR3); /* gate OpenVG */
else
writel(0x3fffffff, ccm_base + CCGR3); /* gate OpenVG, LDB, IPU1, IPU2 */
writel(0xffffffff, ccm_base + CCGR4);
writel(0xffffffff, ccm_base + CCGR5);
writel(0xffff3fff, ccm_base + CCGR6); /* gate VPU */

View File

@ -4,25 +4,39 @@
static inline struct clk *imx_clk_divider(const char *name, const char *parent,
void __iomem *reg, u8 shift, u8 width)
{
return clk_divider(name, parent, reg, shift, width);
return clk_divider(name, parent, reg, shift, width, CLK_SET_RATE_PARENT);
}
static inline struct clk *imx_clk_divider_table(const char *name,
const char *parent, void __iomem *reg, u8 shift, u8 width,
const struct clk_div_table *table)
{
return clk_divider_table(name, parent, reg, shift, width, table,
CLK_SET_RATE_PARENT);
}
static inline struct clk *imx_clk_fixed_factor(const char *name,
const char *parent, unsigned int mult, unsigned int div)
{
return clk_fixed_factor(name, parent, mult, div);
return clk_fixed_factor(name, parent, mult, div, CLK_SET_RATE_PARENT);
}
static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, u8 num_parents)
{
return clk_mux(name, reg, shift, width, parents, num_parents);
return clk_mux(name, reg, shift, width, parents, num_parents, 0);
}
static inline struct clk *imx_clk_mux_p(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, u8 num_parents)
{
return clk_mux(name, reg, shift, width, parents, num_parents, CLK_SET_RATE_PARENT);
}
static inline struct clk *imx_clk_gate(const char *name, const char *parent,
void __iomem *reg, u8 shift)
{
return clk_gate(name, parent, reg, shift);
return clk_gate(name, parent, reg, shift, CLK_SET_RATE_PARENT);
}
struct clk *imx_clk_pllv1(const char *name, const char *parent,

View File

@ -15,6 +15,7 @@
#include <common.h>
#include <io.h>
#include <sizes.h>
#include <mfd/imx6q-iomuxc-gpr.h>
#include <mach/imx6.h>
#include <mach/generic.h>
#include <mach/revision.h>
@ -28,7 +29,9 @@ void imx6_init_lowlevel(void)
{
void __iomem *aips1 = (void *)MX6_AIPS1_ON_BASE_ADDR;
void __iomem *aips2 = (void *)MX6_AIPS2_ON_BASE_ADDR;
void __iomem *iomux = (void *)MX6_IOMUXC_BASE_ADDR;
int is_imx6q = __imx6_cpu_type() == IMX6_CPUTYPE_IMX6Q;
uint32_t val;
/*
* Set all MPROTx to be non-bufferable, trusted for R/W,
@ -87,6 +90,22 @@ void imx6_init_lowlevel(void)
BM_ANADIG_PFD_528_PFD0_CLKGATE,
MX6_ANATOP_BASE_ADDR + HW_ANADIG_PFD_528_CLR);
val = readl(iomux + IOMUXC_GPR4);
val |= IMX6Q_GPR4_VPU_WR_CACHE_SEL | IMX6Q_GPR4_VPU_RD_CACHE_SEL |
IMX6Q_GPR4_VPU_P_WR_CACHE_VAL | IMX6Q_GPR4_VPU_P_RD_CACHE_VAL_MASK |
IMX6Q_GPR4_IPU_WR_CACHE_CTL | IMX6Q_GPR4_IPU_RD_CACHE_CTL;
writel(val, iomux + IOMUXC_GPR4);
/* Increase IPU read QoS priority */
val = readl(iomux + IOMUXC_GPR6);
val &= ~(IMX6Q_GPR6_IPU1_ID00_RD_QOS_MASK | IMX6Q_GPR6_IPU1_ID01_RD_QOS_MASK);
val |= (0xf << 16) | (0x7 << 20);
writel(val, iomux + IOMUXC_GPR6);
val = readl(iomux + IOMUXC_GPR7);
val &= ~(IMX6Q_GPR7_IPU2_ID00_RD_QOS_MASK | IMX6Q_GPR7_IPU2_ID01_RD_QOS_MASK);
val |= (0xf << 16) | (0x7 << 20);
writel(val, iomux + IOMUXC_GPR7);
}
int imx6_init(void)

View File

@ -26,6 +26,9 @@
#define MX6_SPBA_BASE_ADDR (MX6_ATZ1_BASE_ADDR + 0x3C000)
#define MX6_VPU_BASE_ADDR (MX6_ATZ1_BASE_ADDR + 0x40000)
#define MX6_IPU1_BASE_ADDR 0x02400000
#define MX6_IPU2_BASE_ADDR 0x02800000
/* ATZ#1- On Platform */
#define MX6_AIPS1_ON_BASE_ADDR (MX6_ATZ1_BASE_ADDR + 0x7C000)

View File

@ -374,11 +374,11 @@ static int zynq_clock_probe(struct device_d *dev)
clks[uart_clk] = zynq_periph_clk("uart_clk", slcr_base + 0x154);
clks[uart0] = clk_gate("uart0", "uart_clk", slcr_base + 0x154, 0);
clks[uart1] = clk_gate("uart1", "uart_clk", slcr_base + 0x154, 1);
clks[uart0] = clk_gate("uart0", "uart_clk", slcr_base + 0x154, 0, 0);
clks[uart1] = clk_gate("uart1", "uart_clk", slcr_base + 0x154, 1, 0);
clks[gem0] = clk_gate("gem0", "io_pll", slcr_base + 0x140, 0);
clks[gem1] = clk_gate("gem1", "io_pll", slcr_base + 0x144, 1);
clks[gem0] = clk_gate("gem0", "io_pll", slcr_base + 0x140, 0, 0);
clks[gem1] = clk_gate("gem1", "io_pll", slcr_base + 0x144, 1, 0);
clks[cpu_clk] = zynq_cpu_clk("cpu_clk", slcr_base + 0x120);

View File

@ -1,5 +1,5 @@
obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \
clk-mux.o clk-gate.o clk-divider-table.o
clk-mux.o clk-gate.o
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_ARCH_MVEBU) += mvebu/

View File

@ -1,119 +0,0 @@
/*
* clk-divider-table.c - generic barebox clock support. Based on Linux clk support
*
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* 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.
*
*/
#include <common.h>
#include <io.h>
#include <malloc.h>
#include <linux/clk.h>
#include <linux/err.h>
struct clk_divider_table {
struct clk clk;
u8 shift;
u8 width;
void __iomem *reg;
const char *parent;
const struct clk_div_table *table;
int table_size;
int max_div_index;
};
static int clk_divider_set_rate(struct clk *clk, unsigned long rate,
unsigned long parent_rate)
{
struct clk_divider_table *div =
container_of(clk, struct clk_divider_table, clk);
unsigned int val;
int i, div_index = -1;
unsigned long best = 0;
if (rate > parent_rate)
rate = parent_rate;
if (rate < parent_rate / div->table[div->max_div_index].div)
rate = parent_rate / div->table[div->max_div_index].div;
for (i = 0; i < div->table_size; i++) {
unsigned long now = parent_rate / div->table[i].div;
if (now <= rate && now >= best) {
best = now;
div_index = i;
}
}
val = readl(div->reg);
val &= ~(((1 << div->width) - 1) << div->shift);
val |= div_index << div->shift;
writel(val, div->reg);
return 0;
}
static unsigned long clk_divider_recalc_rate(struct clk *clk,
unsigned long parent_rate)
{
struct clk_divider_table *div =
container_of(clk, struct clk_divider_table, clk);
unsigned int val;
val = readl(div->reg) >> div->shift;
val &= (1 << div->width) - 1;
if (val >= div->table_size)
return 0;
return parent_rate / div->table[val].div;
}
static struct clk_ops clk_divider_table_ops = {
.set_rate = clk_divider_set_rate,
.recalc_rate = clk_divider_recalc_rate,
};
struct clk *clk_divider_table(const char *name,
const char *parent, void __iomem *reg, u8 shift, u8 width,
const struct clk_div_table *table)
{
struct clk_divider_table *div = xzalloc(sizeof(*div));
const struct clk_div_table *clkt;
int ret, max_div = 0;
div->shift = shift;
div->reg = reg;
div->width = width;
div->parent = parent;
div->clk.ops = &clk_divider_table_ops;
div->clk.name = name;
div->clk.parent_names = &div->parent;
div->clk.num_parents = 1;
div->table = table;
for (clkt = div->table; clkt->div; clkt++) {
if (clkt->div > max_div) {
max_div = clkt->div;
div->max_div_index = div->table_size;
}
div->table_size++;
}
ret = clk_register(&div->clk);
if (ret) {
free(div);
return ERR_PTR(ret);
}
return &div->clk;
}

View File

@ -20,65 +20,196 @@
#include <linux/clk.h>
#include <linux/err.h>
static unsigned int clk_divider_maxdiv(struct clk_divider *div)
#define div_mask(d) ((1 << ((d)->width)) - 1)
static unsigned int _get_maxdiv(struct clk_divider *divider)
{
if (div->flags & CLK_DIVIDER_ONE_BASED)
return (1 << div->width) - 1;
return 1 << div->width;
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return div_mask(divider);
return div_mask(divider) + 1;
}
static int clk_divider_set_rate(struct clk *clk, unsigned long rate,
unsigned long parent_rate)
static unsigned int _get_table_div(const struct clk_div_table *table,
unsigned int val)
{
struct clk_divider *div = container_of(clk, struct clk_divider, clk);
unsigned int val, divval;
if (rate > parent_rate)
rate = parent_rate;
if (!rate)
rate = 1;
divval = DIV_ROUND_UP(parent_rate, rate);
if (divval > clk_divider_maxdiv(div))
divval = clk_divider_maxdiv(div);
if (!(div->flags & CLK_DIVIDER_ONE_BASED))
divval--;
val = readl(div->reg);
val &= ~(((1 << div->width) - 1) << div->shift);
val |= divval << div->shift;
writel(val, div->reg);
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
if (clkt->val == val)
return clkt->div;
return 0;
}
static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return val;
if (divider->table)
return _get_table_div(divider->table, val);
return val + 1;
}
static unsigned int _get_table_val(const struct clk_div_table *table,
unsigned int div)
{
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
if (clkt->div == div)
return clkt->val;
return 0;
}
static unsigned int _get_val(struct clk_divider *divider, unsigned int div)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return div;
if (divider->table)
return _get_table_val(divider->table, div);
return div - 1;
}
static unsigned long clk_divider_recalc_rate(struct clk *clk,
unsigned long parent_rate)
{
struct clk_divider *div = container_of(clk, struct clk_divider, clk);
unsigned int val;
struct clk_divider *divider = container_of(clk, struct clk_divider, clk);
unsigned int div, val;
val = readl(div->reg) >> div->shift;
val &= (1 << div->width) - 1;
val = readl(divider->reg) >> divider->shift;
val &= div_mask(divider);
if (div->flags & CLK_DIVIDER_ONE_BASED) {
if (!val)
val++;
} else {
val++;
div = _get_div(divider, val);
return parent_rate / div;
}
/*
* The reverse of DIV_ROUND_UP: The maximum number which
* divided by m is r
*/
#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
static bool _is_valid_table_div(const struct clk_div_table *table,
unsigned int div)
{
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
if (clkt->div == div)
return true;
return false;
}
static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
{
if (divider->table)
return _is_valid_table_div(divider->table, div);
return true;
}
static int clk_divider_bestdiv(struct clk *clk, unsigned long rate,
unsigned long *best_parent_rate)
{
struct clk_divider *divider = container_of(clk, struct clk_divider, clk);
int i, bestdiv = 0;
unsigned long parent_rate, best = 0, now, maxdiv;
unsigned long parent_rate_saved = *best_parent_rate;
if (!rate)
rate = 1;
maxdiv = _get_maxdiv(divider);
if (!(clk->flags & CLK_SET_RATE_PARENT)) {
parent_rate = *best_parent_rate;
bestdiv = DIV_ROUND_UP(parent_rate, rate);
bestdiv = bestdiv == 0 ? 1 : bestdiv;
bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
return bestdiv;
}
return parent_rate / val;
/*
* The maximum divider we can use without overflowing
* unsigned long in rate * i below
*/
maxdiv = min(ULONG_MAX / rate, maxdiv);
for (i = 1; i <= maxdiv; i++) {
if (!_is_valid_div(divider, i))
continue;
if (rate * i == parent_rate_saved) {
/*
* It's the most ideal case if the requested rate can be
* divided from parent clock without needing to change
* parent rate, so return the divider immediately.
*/
*best_parent_rate = parent_rate_saved;
return i;
}
parent_rate = clk_round_rate(clk_get_parent(clk),
MULT_ROUND_UP(rate, i));
now = parent_rate / i;
if (now <= rate && now > best) {
bestdiv = i;
best = now;
*best_parent_rate = parent_rate;
}
}
if (!bestdiv) {
bestdiv = _get_maxdiv(divider);
*best_parent_rate = clk_round_rate(clk_get_parent(clk), 1);
}
return bestdiv;
}
static long clk_divider_round_rate(struct clk *clk, unsigned long rate,
unsigned long *parent_rate)
{
int div;
div = clk_divider_bestdiv(clk, rate, parent_rate);
return *parent_rate / div;
}
static int clk_divider_set_rate(struct clk *clk, unsigned long rate,
unsigned long parent_rate)
{
struct clk_divider *divider = container_of(clk, struct clk_divider, clk);
unsigned int div, value;
u32 val;
if (clk->flags & CLK_SET_RATE_PARENT) {
unsigned long best_parent_rate = parent_rate;
div = clk_divider_bestdiv(clk, rate, &best_parent_rate);
clk_set_rate(clk_get_parent(clk), best_parent_rate);
} else {
div = parent_rate / rate;
}
value = _get_val(divider, div);
if (value > div_mask(divider))
value = div_mask(divider);
val = readl(divider->reg);
val &= ~(div_mask(divider) << divider->shift);
val |= value << divider->shift;
writel(val, divider->reg);
return 0;
}
struct clk_ops clk_divider_ops = {
.set_rate = clk_divider_set_rate,
.recalc_rate = clk_divider_recalc_rate,
.round_rate = clk_divider_round_rate,
};
struct clk *clk_divider(const char *name, const char *parent,
void __iomem *reg, u8 shift, u8 width)
void __iomem *reg, u8 shift, u8 width, unsigned flags)
{
struct clk_divider *div = xzalloc(sizeof(*div));
int ret;
@ -89,6 +220,7 @@ struct clk *clk_divider(const char *name, const char *parent,
div->parent = parent;
div->clk.ops = &clk_divider_ops;
div->clk.name = name;
div->clk.flags = flags;
div->clk.parent_names = &div->parent;
div->clk.num_parents = 1;
@ -102,12 +234,12 @@ struct clk *clk_divider(const char *name, const char *parent,
}
struct clk *clk_divider_one_based(const char *name, const char *parent,
void __iomem *reg, u8 shift, u8 width)
void __iomem *reg, u8 shift, u8 width, unsigned flags)
{
struct clk_divider *div;
struct clk *clk;
clk = clk_divider(name, parent, reg, shift, width);
clk = clk_divider(name, parent, reg, shift, width, flags);
if (IS_ERR(clk))
return clk;
@ -116,3 +248,39 @@ struct clk *clk_divider_one_based(const char *name, const char *parent,
return clk;
}
struct clk *clk_divider_table(const char *name,
const char *parent, void __iomem *reg, u8 shift, u8 width,
const struct clk_div_table *table, unsigned flags)
{
struct clk_divider *div = xzalloc(sizeof(*div));
const struct clk_div_table *clkt;
int ret, max_div = 0;
div->shift = shift;
div->reg = reg;
div->width = width;
div->parent = parent;
div->clk.ops = &clk_divider_ops;
div->clk.name = name;
div->clk.flags = flags;
div->clk.parent_names = &div->parent;
div->clk.num_parents = 1;
div->table = table;
for (clkt = div->table; clkt->div; clkt++) {
if (clkt->div > max_div) {
max_div = clkt->div;
div->max_div_index = div->table_size;
}
div->table_size++;
}
ret = clk_register(&div->clk);
if (ret) {
free(div);
return ERR_PTR(ret);
}
return &div->clk;
}

View File

@ -35,12 +35,42 @@ static unsigned long clk_fixed_factor_recalc_rate(struct clk *clk,
return (parent_rate / f->div) * f->mult;
}
static long clk_factor_round_rate(struct clk *clk, unsigned long rate,
unsigned long *prate)
{
struct clk_fixed_factor *fix = container_of(clk, struct clk_fixed_factor, clk);
if (clk->flags & CLK_SET_RATE_PARENT) {
unsigned long best_parent;
best_parent = (rate / fix->mult) * fix->div;
*prate = clk_round_rate(clk_get_parent(clk), best_parent);
}
return (*prate / fix->div) * fix->mult;
}
static int clk_factor_set_rate(struct clk *clk, unsigned long rate,
unsigned long parent_rate)
{
struct clk_fixed_factor *fix = container_of(clk, struct clk_fixed_factor, clk);
if (clk->flags & CLK_SET_RATE_PARENT) {
printk("%s: %ld -> parent %ld\n", __func__, rate, rate * fix->div / fix->mult);
return clk_set_rate(clk_get_parent(clk), rate * fix->div / fix->mult);
}
return 0;
}
static struct clk_ops clk_fixed_factor_ops = {
.set_rate = clk_factor_set_rate,
.round_rate = clk_factor_round_rate,
.recalc_rate = clk_fixed_factor_recalc_rate,
};
struct clk *clk_fixed_factor(const char *name,
const char *parent, unsigned int mult, unsigned int div)
const char *parent, unsigned int mult, unsigned int div, unsigned flags)
{
struct clk_fixed_factor *f = xzalloc(sizeof(*f));
int ret;
@ -50,6 +80,7 @@ struct clk *clk_fixed_factor(const char *name,
f->parent = parent;
f->clk.ops = &clk_fixed_factor_ops;
f->clk.name = name;
f->clk.flags = flags;
f->clk.parent_names = &f->parent;
f->clk.num_parents = 1;

View File

@ -77,13 +77,15 @@ static int clk_gate_is_enabled(struct clk *clk)
}
static struct clk_ops clk_gate_ops = {
.set_rate = clk_parent_set_rate,
.round_rate = clk_parent_round_rate,
.enable = clk_gate_enable,
.disable = clk_gate_disable,
.is_enabled = clk_gate_is_enabled,
};
struct clk *clk_gate_alloc(const char *name, const char *parent,
void __iomem *reg, u8 shift)
void __iomem *reg, u8 shift, unsigned flags)
{
struct clk_gate *g = xzalloc(sizeof(*g));
@ -92,6 +94,7 @@ struct clk *clk_gate_alloc(const char *name, const char *parent,
g->shift = shift;
g->clk.ops = &clk_gate_ops;
g->clk.name = name;
g->clk.flags = flags;
g->clk.parent_names = &g->parent;
g->clk.num_parents = 1;
@ -106,12 +109,12 @@ void clk_gate_free(struct clk *clk_gate)
}
struct clk *clk_gate(const char *name, const char *parent, void __iomem *reg,
u8 shift)
u8 shift, unsigned flags)
{
struct clk *g;
int ret;
g = clk_gate_alloc(name , parent, reg, shift);
g = clk_gate_alloc(name , parent, reg, shift, flags);
ret = clk_register(g);
if (ret) {
@ -123,12 +126,12 @@ struct clk *clk_gate(const char *name, const char *parent, void __iomem *reg,
}
struct clk *clk_gate_inverted(const char *name, const char *parent,
void __iomem *reg, u8 shift)
void __iomem *reg, u8 shift, unsigned flags)
{
struct clk *clk;
struct clk_gate *g;
clk = clk_gate(name, parent, reg, shift);
clk = clk_gate(name, parent, reg, shift, flags);
if (IS_ERR(clk))
return clk;

View File

@ -51,12 +51,15 @@ static int clk_mux_set_parent(struct clk *clk, u8 idx)
}
static struct clk_ops clk_mux_ops = {
.set_rate = clk_parent_set_rate,
.round_rate = clk_parent_round_rate,
.get_parent = clk_mux_get_parent,
.set_parent = clk_mux_set_parent,
};
struct clk *clk_mux_alloc(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, u8 num_parents)
u8 shift, u8 width, const char **parents, u8 num_parents,
unsigned flags)
{
struct clk_mux *m = xzalloc(sizeof(*m));
@ -65,6 +68,7 @@ struct clk *clk_mux_alloc(const char *name, void __iomem *reg,
m->width = width;
m->clk.ops = &clk_mux_ops;
m->clk.name = name;
m->clk.flags = flags;
m->clk.parent_names = parents;
m->clk.num_parents = num_parents;
@ -79,12 +83,12 @@ void clk_mux_free(struct clk *clk_mux)
}
struct clk *clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, u8 num_parents)
u8 shift, u8 width, const char **parents, u8 num_parents, unsigned flags)
{
struct clk *m;
int ret;
m = clk_mux_alloc(name, reg, shift, width, parents, num_parents);
m = clk_mux_alloc(name, reg, shift, width, parents, num_parents, flags);
ret = clk_register(m);
if (ret) {

View File

@ -104,9 +104,19 @@ unsigned long clk_get_rate(struct clk *clk)
long clk_round_rate(struct clk *clk, unsigned long rate)
{
unsigned long parent_rate = 0;
struct clk *parent;
if (IS_ERR(clk))
return 0;
parent = clk_get_parent(clk);
if (parent)
parent_rate = clk_get_rate(parent);
if (clk->ops->round_rate)
return clk->ops->round_rate(clk, rate, &parent_rate);
return clk_get_rate(clk);
}
@ -249,11 +259,31 @@ int clk_is_enabled(struct clk *clk)
return clk_is_enabled(clk);
}
/*
* Generic struct clk_ops callbacks
*/
int clk_is_enabled_always(struct clk *clk)
{
return 1;
}
long clk_parent_round_rate(struct clk *clk, unsigned long rate,
unsigned long *prate)
{
if (!(clk->flags & CLK_SET_RATE_PARENT))
return *prate;
return clk_round_rate(clk_get_parent(clk), rate);
}
int clk_parent_set_rate(struct clk *clk, unsigned long rate,
unsigned long parent_rate)
{
if (!(clk->flags & CLK_SET_RATE_PARENT))
return 0;
return clk_set_rate(clk_get_parent(clk), rate);
}
#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER)
/**
* struct of_clk_provider - Clock provider registration structure

View File

@ -87,7 +87,7 @@ int mvebu_coreclk_probe(struct device_d *dev)
2+n, &rclk_name);
desc->get_clk_ratio(base, desc->ratios[n].id, &mult, &div);
clk_data.clks[2+n] = clk_fixed_factor(rclk_name, cpuclk_name,
mult, div);
mult, div, 0);
WARN_ON(IS_ERR(clk_data.clks[2+n]));
};
@ -188,7 +188,7 @@ int mvebu_clk_gating_probe(struct device_d *dev)
(desc[n].parent) ? desc[n].parent : default_parent;
gate->bit_idx = desc[n].bit_idx;
gate->clk = clk_gate(desc[n].name, parent,
base, desc[n].bit_idx);
base, desc[n].bit_idx, 0);
WARN_ON(IS_ERR(gate->clk));
}

View File

@ -128,7 +128,7 @@ int __init mx28_clocks_init(void __iomem *regs)
clks[fec] = mxs_clk_gate("fec", "fec_sleep", ENET, 30);
clks[usb0_phy] = mxs_clk_gate("usb0_phy", "pll0", PLL0CTRL0, 18);
clks[usb1_phy] = mxs_clk_gate("usb1_phy", "pll1", PLL1CTRL0, 18);
clks[enet_out] = clk_gate("enet_out", "pll2", ENET, 18);
clks[enet_out] = clk_gate("enet_out", "pll2", ENET, 18, 0);
clks[lcdif_comp] = mxs_clk_lcdif("lcdif_comp", clks[ref_pix],
clks[lcdif_div], clks[lcdif]);

View File

@ -34,19 +34,19 @@ static inline struct clk *mxs_clk_fixed(const char *name, int rate)
static inline struct clk *mxs_clk_gate(const char *name,
const char *parent_name, void __iomem *reg, u8 shift)
{
return clk_gate_inverted(name, parent_name, reg, shift);
return clk_gate_inverted(name, parent_name, reg, shift, 0);
}
static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parent_names, int num_parents)
{
return clk_mux(name, reg, shift, width, parent_names, num_parents);
return clk_mux(name, reg, shift, width, parent_names, num_parents, 0);
}
static inline struct clk *mxs_clk_fixed_factor(const char *name,
const char *parent_name, unsigned int mult, unsigned int div)
{
return clk_fixed_factor(name, parent_name, mult, div);
return clk_fixed_factor(name, parent_name, mult, div, 0);
}
#endif /* __MXS_CLK_H */

View File

@ -140,12 +140,12 @@ struct clk *_tegra_clk_register_periph(const char *name,
}
periph->mux = clk_mux_alloc(NULL, clk_base + reg_offset, 30, 2,
parent_names, num_parents);
parent_names, num_parents, 0);
if (!periph->mux)
goto out_mux;
periph->gate = clk_gate_alloc(NULL, NULL, clk_base + 0x10 +
((id >> 3) & 0xc), id & 0x1f);
((id >> 3) & 0xc), id & 0x1f, 0);
if (!periph->gate)
goto out_gate;

View File

@ -62,7 +62,7 @@ static void tegra20_osc_clk_init(void)
clks[clk_32k] = clk_fixed("clk_32k", 32768);
clks[pll_ref] = clk_fixed_factor("pll_ref", "clk_m", 1,
get_pll_ref_div());
get_pll_ref_div(), 0);
}
/* PLL frequency tables */

View File

@ -587,4 +587,4 @@ static struct driver_d i2c_fsl_driver = {
.name = DRIVER_NAME,
.of_compatible = DRV_OF_COMPAT(imx_i2c_dt_ids),
};
device_platform_driver(i2c_fsl_driver);
coredevice_platform_driver(i2c_fsl_driver);

View File

@ -405,6 +405,17 @@ struct i2c_adapter *i2c_get_adapter(int busnum)
return NULL;
}
struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
{
struct i2c_adapter *adap;
list_for_each_entry(adap, &adapter_list, list)
if (adap->dev.device_node == node)
return adap;
return NULL;
}
/**
* i2c_add_numbered_adapter - declare i2c adapter, use static bus number
* @adapter: the adapter to register (with adap->nr initialized)

View File

@ -77,6 +77,8 @@ config DRIVER_VIDEO_BCM2835
help
Add support for the BCM2835/VideoCore frame buffer device.
source drivers/video/imx-ipu-v3/Kconfig
config DRIVER_VIDEO_SIMPLEFB
bool "Simple framebuffer support"
depends on OFTREE
@ -84,4 +86,10 @@ config DRIVER_VIDEO_SIMPLEFB
Add support for setting up the kernel's simple framebuffer driver
based on the active barebox framebuffer.
config DRIVER_VIDEO_EDID
bool "Add EDID support"
help
This enabled support for reading and parsing EDID data from an attached
monitor.
endif

View File

@ -1,4 +1,6 @@
obj-$(CONFIG_VIDEO) += fb.o
obj-$(CONFIG_DRIVER_VIDEO_EDID) += edid.o
obj-$(CONFIG_OFDEVICE) += of_display_timing.o
obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
@ -11,3 +13,4 @@ obj-$(CONFIG_DRIVER_VIDEO_SDL) += sdl.o
obj-$(CONFIG_DRIVER_VIDEO_OMAP) += omap.o
obj-$(CONFIG_DRIVER_VIDEO_BCM2835) += bcm2835.o
obj-$(CONFIG_DRIVER_VIDEO_SIMPLEFB) += simplefb.o
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += imx-ipu-v3/

View File

@ -269,9 +269,9 @@ int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data)
info = &sinfo->info;
info->priv = sinfo;
info->fbops = &atmel_lcdc_ops;
info->mode_list = pdata->mode_list;
info->num_modes = pdata->num_modes;
info->mode = &info->mode_list[0];
info->modes.modes = pdata->mode_list;
info->modes.num_modes = pdata->num_modes;
info->mode = &info->modes.modes[0];
info->xres = info->mode->xres;
info->yres = info->mode->yres;
info->bits_per_pixel = pdata->default_bpp;

909
drivers/video/edid.c Normal file
View File

@ -0,0 +1,909 @@
/*
* drivers/video/edid.c
*
* Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
*
* Credits:
*
* The EDID Parser is a conglomeration from the following sources:
*
* 1. SciTech SNAP Graphics Architecture
* Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved.
*
* 2. XFree86 4.3.0, interpret_edid.c
* Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
*
* 3. John Fremlin <vii@users.sourceforge.net> and
* Ani Joshi <ajoshi@unixbox.com>
*
* Generalized Timing Formula is derived from:
*
* GTF Spreadsheet by Andy Morrish (1/5/97)
* available at http://www.vesa.org
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
*/
#define pr_fmt(fmt) "EDID: " fmt
#include <common.h>
#include <fb.h>
#include <malloc.h>
#include <i2c/i2c.h>
#include "edid.h"
#define FBMON_FIX_HEADER 1
#define FBMON_FIX_INPUT 2
#define FBMON_FIX_TIMINGS 3
struct broken_edid {
u8 manufacturer[4];
u32 model;
u32 fix;
};
static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00
};
static int edid_is_serial_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00) &&
(block[2] == 0x00) && (block[3] == 0xff) &&
(block[4] == 0x00))
return 1;
else
return 0;
}
static int edid_is_ascii_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00) &&
(block[2] == 0x00) && (block[3] == 0xfe) &&
(block[4] == 0x00))
return 1;
else
return 0;
}
static int edid_is_limits_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00) &&
(block[2] == 0x00) && (block[3] == 0xfd) &&
(block[4] == 0x00))
return 1;
else
return 0;
}
static int edid_is_monitor_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00) &&
(block[2] == 0x00) && (block[3] == 0xfc) &&
(block[4] == 0x00))
return 1;
else
return 0;
}
static int edid_is_timing_block(unsigned char *block)
{
if ((block[0] != 0x00) || (block[1] != 0x00) ||
(block[2] != 0x00) || (block[4] != 0x00))
return 1;
else
return 0;
}
static int check_edid(unsigned char *edid)
{
unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4];
unsigned char *b;
u32 model;
int i, fix = 0, ret = 0;
manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
manufacturer[1] = ((block[0] & 0x03) << 3) +
((block[1] & 0xe0) >> 5) + '@';
manufacturer[2] = (block[1] & 0x1f) + '@';
manufacturer[3] = 0;
model = block[2] + (block[3] << 8);
switch (fix) {
case FBMON_FIX_HEADER:
for (i = 0; i < 8; i++) {
if (edid[i] != edid_v1_header[i]) {
ret = fix;
break;
}
}
break;
case FBMON_FIX_INPUT:
b = edid + EDID_STRUCT_DISPLAY;
/* Only if display is GTF capable will
the input type be reset to analog */
if (b[4] & 0x01 && b[0] & 0x80)
ret = fix;
break;
case FBMON_FIX_TIMINGS:
b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
ret = fix;
for (i = 0; i < 4; i++) {
if (edid_is_limits_block(b)) {
ret = 0;
break;
}
b += DETAILED_TIMING_DESCRIPTION_SIZE;
}
break;
}
if (ret)
printk("fbmon: The EDID Block of "
"Manufacturer: %s Model: 0x%x is known to "
"be broken,\n", manufacturer, model);
return ret;
}
static void fix_edid(unsigned char *edid, int fix)
{
int i;
unsigned char *b, csum = 0;
switch (fix) {
case FBMON_FIX_HEADER:
printk("fbmon: trying a header reconstruct\n");
memcpy(edid, edid_v1_header, 8);
break;
case FBMON_FIX_INPUT:
printk("fbmon: trying to fix input type\n");
b = edid + EDID_STRUCT_DISPLAY;
b[0] &= ~0x80;
edid[127] += 0x80;
break;
case FBMON_FIX_TIMINGS:
printk("fbmon: trying to fix monitor timings\n");
b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
for (i = 0; i < 4; i++) {
if (!(edid_is_serial_block(b) ||
edid_is_ascii_block(b) ||
edid_is_monitor_block(b) ||
edid_is_timing_block(b))) {
b[0] = 0x00;
b[1] = 0x00;
b[2] = 0x00;
b[3] = 0xfd;
b[4] = 0x00;
b[5] = 60; /* vfmin */
b[6] = 60; /* vfmax */
b[7] = 30; /* hfmin */
b[8] = 75; /* hfmax */
b[9] = 17; /* pixclock - 170 MHz*/
b[10] = 0; /* GTF */
break;
}
b += DETAILED_TIMING_DESCRIPTION_SIZE;
}
for (i = 0; i < EDID_LENGTH - 1; i++)
csum += edid[i];
edid[127] = 256 - csum;
break;
}
}
static int edid_checksum(unsigned char *edid)
{
unsigned char csum = 0, all_null = 0;
int i, err = 0, fix = check_edid(edid);
if (fix)
fix_edid(edid, fix);
for (i = 0; i < EDID_LENGTH; i++) {
csum += edid[i];
all_null |= edid[i];
}
if (csum == 0x00 && all_null) {
/* checksum passed, everything's good */
err = 1;
}
return err;
}
static int edid_check_header(unsigned char *edid)
{
int i, err = 1, fix = check_edid(edid);
if (fix)
fix_edid(edid, fix);
for (i = 0; i < 8; i++) {
if (edid[i] != edid_v1_header[i])
err = 0;
}
return err;
}
/*
* VESA Generalized Timing Formula (GTF)
*/
#define FLYBACK 550
#define V_FRONTPORCH 1
#define H_OFFSET 40
#define H_SCALEFACTOR 20
#define H_BLANKSCALE 128
#define H_GRADIENT 600
#define C_VAL 30
#define M_VAL 300
struct __fb_timings {
u32 dclk;
u32 hfreq;
u32 vfreq;
u32 hactive;
u32 vactive;
u32 hblank;
u32 vblank;
u32 htotal;
u32 vtotal;
};
/**
* fb_get_vblank - get vertical blank time
* @hfreq: horizontal freq
*
* DESCRIPTION:
* vblank = right_margin + vsync_len + left_margin
*
* given: right_margin = 1 (V_FRONTPORCH)
* vsync_len = 3
* flyback = 550
*
* flyback * hfreq
* left_margin = --------------- - vsync_len
* 1000000
*/
static u32 fb_get_vblank(u32 hfreq)
{
u32 vblank;
vblank = (hfreq * FLYBACK)/1000;
vblank = (vblank + 500)/1000;
return (vblank + V_FRONTPORCH);
}
/**
* fb_get_hblank_by_freq - get horizontal blank time given hfreq
* @hfreq: horizontal freq
* @xres: horizontal resolution in pixels
*
* DESCRIPTION:
*
* xres * duty_cycle
* hblank = ------------------
* 100 - duty_cycle
*
* duty cycle = percent of htotal assigned to inactive display
* duty cycle = C - (M/Hfreq)
*
* where: C = ((offset - scale factor) * blank_scale)
* -------------------------------------- + scale factor
* 256
* M = blank_scale * gradient
*
*/
static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres)
{
u32 c_val, m_val, duty_cycle, hblank;
c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 +
H_SCALEFACTOR) * 1000;
m_val = (H_BLANKSCALE * H_GRADIENT)/256;
m_val = (m_val * 1000000)/hfreq;
duty_cycle = c_val - m_val;
hblank = (xres * duty_cycle)/(100000 - duty_cycle);
return (hblank);
}
/**
* int_sqrt - rough approximation to sqrt
* @x: integer of which to calculate the sqrt
*
* A very rough approximation to the sqrt() function.
*/
unsigned long int_sqrt(unsigned long x)
{
unsigned long b, m, y = 0;
if (x <= 1)
return x;
m = 1UL << (BITS_PER_LONG - 2);
while (m != 0) {
b = y + m;
y >>= 1;
if (x >= b) {
x -= b;
y += m;
}
m >>= 2;
}
return y;
}
EXPORT_SYMBOL(int_sqrt);
/**
* fb_get_hfreq - estimate hsync
* @vfreq: vertical refresh rate
* @yres: vertical resolution
*
* DESCRIPTION:
*
* (yres + front_port) * vfreq * 1000000
* hfreq = -------------------------------------
* (1000000 - (vfreq * FLYBACK)
*
*/
static u32 fb_get_hfreq(u32 vfreq, u32 yres)
{
u32 divisor, hfreq;
divisor = (1000000 - (vfreq * FLYBACK))/1000;
hfreq = (yres + V_FRONTPORCH) * vfreq * 1000;
return (hfreq/divisor);
}
static void fb_timings_vfreq(struct __fb_timings *timings)
{
timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive);
timings->vblank = fb_get_vblank(timings->hfreq);
timings->vtotal = timings->vactive + timings->vblank;
timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
timings->hactive);
timings->htotal = timings->hactive + timings->hblank;
timings->dclk = timings->htotal * timings->hfreq;
}
/*
* fb_get_mode - calculates video mode using VESA GTF
* @flags: if: 0 - maximize vertical refresh rate
* 1 - vrefresh-driven calculation;
* 2 - hscan-driven calculation;
* 3 - pixelclock-driven calculation;
* @val: depending on @flags, ignored, vrefresh, hsync or pixelclock
* @var: pointer to fb_var_screeninfo
* @info: pointer to fb_info
*
* DESCRIPTION:
* Calculates video mode based on monitor specs using VESA GTF.
* The GTF is best for VESA GTF compliant monitors but is
* specifically formulated to work for older monitors as well.
*
* If @flag==0, the function will attempt to maximize the
* refresh rate. Otherwise, it will calculate timings based on
* the flag and accompanying value.
*
* If FB_IGNOREMON bit is set in @flags, monitor specs will be
* ignored and @var will be filled with the calculated timings.
*
* All calculations are based on the VESA GTF Spreadsheet
* available at VESA's public ftp (http://www.vesa.org).
*
* NOTES:
* The timings generated by the GTF will be different from VESA
* DMT. It might be a good idea to keep a table of standard
* VESA modes as well. The GTF may also not work for some displays,
* such as, and especially, analog TV.
*
* REQUIRES:
* A valid info->monspecs, otherwise 'safe numbers' will be used.
*/
int fb_get_mode(int flags, u32 val, struct fb_videomode *var)
{
struct __fb_timings *timings;
u32 interlace = 1, dscan = 1;
u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0;
timings = xzalloc(sizeof(struct __fb_timings));
/*
* If monspecs are invalid, use values that are enough
* for 640x480@60
*/
hfmin = 29000; hfmax = 30000;
vfmin = 60; vfmax = 60;
dclkmin = 0; dclkmax = 25000000;
timings->hactive = var->xres;
timings->vactive = var->yres;
if (var->vmode & FB_VMODE_INTERLACED) {
timings->vactive /= 2;
interlace = 2;
}
if (var->vmode & FB_VMODE_DOUBLE) {
timings->vactive *= 2;
dscan = 2;
}
/* vrefresh driven */
timings->vfreq = val;
fb_timings_vfreq(timings);
if (timings->dclk)
var->pixclock = KHZ2PICOS(timings->dclk / 1000);
var->hsync_len = (timings->htotal * 8) / 100;
var->right_margin = (timings->hblank / 2) - var->hsync_len;
var->left_margin = timings->hblank - var->right_margin -
var->hsync_len;
var->vsync_len = (3 * interlace) / dscan;
var->lower_margin = (1 * interlace) / dscan;
var->upper_margin = (timings->vblank * interlace) / dscan -
(var->vsync_len + var->lower_margin);
free(timings);
return err;
}
static void calc_mode_timings(int xres, int yres, int refresh,
struct fb_videomode *mode)
{
mode->xres = xres;
mode->yres = yres;
mode->refresh = refresh;
fb_get_mode(0, refresh, mode);
mode->name = asprintf("%dx%d@%d-calc", mode->xres, mode->yres, mode->refresh);
pr_debug(" %s\n", mode->name);
}
const struct fb_videomode vesa_modes[] = {
/* 0 640x350-85 VESA */
{ NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3,
FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, 0},
/* 1 640x400-85 VESA */
{ NULL, 85, 640, 400, 31746, 96, 32, 41, 01, 64, 3,
FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
/* 2 720x400-85 VESA */
{ NULL, 85, 721, 400, 28169, 108, 36, 42, 01, 72, 3,
FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
/* 3 640x480-60 VESA */
{ NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
0, FB_VMODE_NONINTERLACED, 0 },
/* 4 640x480-72 VESA */
{ NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2,
0, FB_VMODE_NONINTERLACED, 0 },
/* 5 640x480-75 VESA */
{ NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3,
0, FB_VMODE_NONINTERLACED, 0 },
/* 6 640x480-85 VESA */
{ NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3,
0, FB_VMODE_NONINTERLACED, 0 },
/* 7 800x600-56 VESA */
{ NULL, 56, 800, 600, 27777, 128, 24, 22, 01, 72, 2,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 8 800x600-60 VESA */
{ NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 9 800x600-72 VESA */
{ NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 10 800x600-75 VESA */
{ NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 11 800x600-85 VESA */
{ NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 12 1024x768i-43 VESA */
{ NULL, 43, 1024, 768, 22271, 56, 8, 41, 0, 176, 8,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_INTERLACED, 0 },
/* 13 1024x768-60 VESA */
{ NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
0, FB_VMODE_NONINTERLACED, 0 },
/* 14 1024x768-70 VESA */
{ NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
0, FB_VMODE_NONINTERLACED, 0 },
/* 15 1024x768-75 VESA */
{ NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 16 1024x768-85 VESA */
{ NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 17 1152x864-75 VESA */
{ NULL, 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 18 1280x960-60 VESA */
{ NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 19 1280x960-85 VESA */
{ NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 20 1280x1024-60 VESA */
{ NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 21 1280x1024-75 VESA */
{ NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 22 1280x1024-85 VESA */
{ NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 23 1600x1200-60 VESA */
{ NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 24 1600x1200-65 VESA */
{ NULL, 65, 1600, 1200, 5698, 304, 64, 46, 1, 192, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 25 1600x1200-70 VESA */
{ NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 26 1600x1200-75 VESA */
{ NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 27 1600x1200-85 VESA */
{ NULL, 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, 0 },
/* 28 1792x1344-60 VESA */
{ NULL, 60, 1792, 1344, 4882, 328, 128, 46, 1, 200, 3,
FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
/* 29 1792x1344-75 VESA */
{ NULL, 75, 1792, 1344, 3831, 352, 96, 69, 1, 216, 3,
FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
/* 30 1856x1392-60 VESA */
{ NULL, 60, 1856, 1392, 4580, 352, 96, 43, 1, 224, 3,
FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
/* 31 1856x1392-75 VESA */
{ NULL, 75, 1856, 1392, 3472, 352, 128, 104, 1, 224, 3,
FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
/* 32 1920x1440-60 VESA */
{ NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 200, 3,
FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
/* 33 1920x1440-75 VESA */
{ NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3,
FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
};
#define VESA_MODEDB_SIZE ARRAY_SIZE(vesa_modes)
static void add_vesa_mode(struct fb_videomode *mode, int num)
{
*mode = vesa_modes[num];
mode->name = asprintf("%dx%d@%d-vesa", mode->xres, mode->yres, mode->refresh);
pr_debug(" %s\n", mode->name);
}
static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
{
int num = 0;
unsigned char c;
c = block[0];
if (c & 0x80)
calc_mode_timings(720, 400, 70, &mode[num++]);
if (c & 0x40)
calc_mode_timings(720, 400, 88, &mode[num++]);
if (c & 0x20)
add_vesa_mode(&mode[num++], 3);
if (c & 0x10)
calc_mode_timings(640, 480, 67, &mode[num++]);
if (c & 0x08)
add_vesa_mode(&mode[num++], 4);
if (c & 0x04)
add_vesa_mode(&mode[num++], 5);
if (c & 0x02)
add_vesa_mode(&mode[num++], 7);
if (c & 0x01)
add_vesa_mode(&mode[num++], 8);
c = block[1];
if (c & 0x80)
add_vesa_mode(&mode[num++], 9);
if (c & 0x40)
add_vesa_mode(&mode[num++], 10);
if (c & 0x20)
calc_mode_timings(832, 624, 75, &mode[num++]);
if (c & 0x10)
add_vesa_mode(&mode[num++], 12);
if (c & 0x08)
add_vesa_mode(&mode[num++], 13);
if (c & 0x04)
add_vesa_mode(&mode[num++], 14);
if (c & 0x02)
add_vesa_mode(&mode[num++], 15);
if (c & 0x01)
add_vesa_mode(&mode[num++], 21);
c = block[2];
if (c & 0x80)
add_vesa_mode(&mode[num++], 17);
pr_debug(" Manufacturer's mask: %x\n",c & 0x7F);
return num;
}
static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
int ver, int rev)
{
int xres, yres = 0, refresh, ratio, i;
xres = (block[0] + 31) * 8;
if (xres <= 256)
return 0;
ratio = (block[1] & 0xc0) >> 6;
switch (ratio) {
case 0:
/* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */
if (ver < 1 || (ver == 1 && rev < 3))
yres = xres;
else
yres = (xres * 10) / 16;
break;
case 1:
yres = (xres * 3) / 4;
break;
case 2:
yres = (xres * 4) / 5;
break;
case 3:
yres = (xres * 9) / 16;
break;
}
refresh = (block[1] & 0x3f) + 60;
for (i = 0; i < VESA_MODEDB_SIZE; i++) {
if (vesa_modes[i].xres == xres &&
vesa_modes[i].yres == yres &&
vesa_modes[i].refresh == refresh) {
add_vesa_mode(mode, i);
return 1;
}
}
calc_mode_timings(xres, yres, refresh, mode);
return 1;
}
static int get_dst_timing(unsigned char *block,
struct fb_videomode *mode, int ver, int rev)
{
int j, num = 0;
for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE)
num += get_std_timing(block, &mode[num], ver, rev);
return num;
}
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;
}
pr_debug(" %d MHz ", PIXEL_CLOCK/1000000);
pr_debug("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
pr_debug("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
pr_debug("%sHSync %sVSync\n", (HSYNC_POSITIVE) ? "+" : "-",
(VSYNC_POSITIVE) ? "+" : "-");
mode->name = asprintf("%dx%d@%d", mode->xres, mode->yres, mode->refresh);
}
/**
* edid_to_display_timings - create video mode database
* @edid: EDID data
* @dbsize: database size
*
* RETURNS: struct fb_videomode, @dbsize contains length of database
*
* DESCRIPTION:
* This function builds a mode database using the contents of the EDID
* data
*/
int edid_to_display_timings(struct display_timings *timings, unsigned char *edid)
{
struct fb_videomode *mode;
unsigned char *block;
int num = 0, i, first = 1;
int ver, rev, ret;
ver = edid[EDID_STRUCT_VERSION];
rev = edid[EDID_STRUCT_REVISION];
mode = xzalloc(50 * sizeof(struct fb_videomode));
if (!edid_checksum(edid) ||
!edid_check_header(edid)) {
ret = -EINVAL;
goto out;
}
pr_debug(" Detailed Timings\n");
block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
if (!(block[0] == 0x00 && block[1] == 0x00)) {
get_detailed_timing(block, &mode[num]);
if (first) {
first = 0;
}
num++;
}
}
pr_debug(" Supported VESA Modes\n");
block = edid + ESTABLISHED_TIMING_1;
num += get_est_timing(block, &mode[num]);
pr_debug(" Standard Timings\n");
block = edid + STD_TIMING_DESCRIPTIONS_START;
for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
num += get_std_timing(block, &mode[num], ver, rev);
block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa)
num += get_dst_timing(block + 5, &mode[num], ver, rev);
}
/* Yikes, EDID data is totally useless */
if (!num) {
free(mode);
return -EINVAL;
}
timings->num_modes = num;
timings->modes = mode;
return 0;
out:
free(timings);
free(mode);
return ret;
}
#define DDC_ADDR 0x50
#define DDC_SEGMENT_ADDR 0x30
/**
* Get EDID information via I2C.
*
* \param adapter : i2c device adaptor
* \param buf : EDID data buffer to be filled
* \param len : EDID data buffer length
* \return 0 on success or -1 on failure.
*
* Try to fetch EDID information by calling i2c driver function.
*/
static int
edid_do_read_i2c(struct i2c_adapter *adapter, unsigned char *buf,
int block, int len)
{
unsigned char start = block * EDID_LENGTH;
unsigned char segment = block >> 1;
unsigned char xfers = segment ? 3 : 2;
int ret, retries = 5;
/* The core i2c driver will automatically retry the transfer if the
* adapter reports EAGAIN. However, we find that bit-banging transfers
* are susceptible to errors under a heavily loaded machine and
* generate spurious NAKs and timeouts. Retrying the transfer
* of the individual block a few times seems to overcome this.
*/
do {
struct i2c_msg msgs[] = {
{
.addr = DDC_SEGMENT_ADDR,
.flags = 0,
.len = 1,
.buf = &segment,
}, {
.addr = DDC_ADDR,
.flags = 0,
.len = 1,
.buf = &start,
}, {
.addr = DDC_ADDR,
.flags = I2C_M_RD,
.len = len,
.buf = buf,
}
};
/*
* Avoid sending the segment addr to not upset non-compliant ddc
* monitors.
*/
ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
} while (ret != xfers && --retries);
return ret == xfers ? 0 : -1;
}
void *edid_read_i2c(struct i2c_adapter *adapter)
{
u8 *block;
block = xmalloc(EDID_LENGTH);
if (edid_do_read_i2c(adapter, block, 0, EDID_LENGTH))
goto out;
return block;
out:
free(block);
return NULL;
}
void fb_edid_add_modes(struct fb_info *info)
{
if (info->edid_i2c_adapter)
info->edid_data = edid_read_i2c(info->edid_i2c_adapter);
if (!info->edid_data)
return;
edid_to_display_timings(&info->edid_modes, info->edid_data);
}

138
drivers/video/edid.h Normal file
View File

@ -0,0 +1,138 @@
/*
* drivers/video/edid.h - EDID/DDC Header
*
* Based on:
* 1. XFree86 4.3.0, edid.h
* Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
*
* 2. John Fremlin <vii@users.sourceforge.net> and
* Ani Joshi <ajoshi@unixbox.com>
*
* DDC is a Trademark of VESA (Video Electronics Standard Association).
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#ifndef __EDID_H__
#define __EDID_H__
#define EDID_LENGTH 0x80
#define EDID_HEADER 0x00
#define EDID_HEADER_END 0x07
#define ID_MANUFACTURER_NAME 0x08
#define ID_MANUFACTURER_NAME_END 0x09
#define ID_MODEL 0x0a
#define ID_SERIAL_NUMBER 0x0c
#define MANUFACTURE_WEEK 0x10
#define MANUFACTURE_YEAR 0x11
#define EDID_STRUCT_VERSION 0x12
#define EDID_STRUCT_REVISION 0x13
#define EDID_STRUCT_DISPLAY 0x14
#define DPMS_FLAGS 0x18
#define ESTABLISHED_TIMING_1 0x23
#define ESTABLISHED_TIMING_2 0x24
#define MANUFACTURERS_TIMINGS 0x25
/* standard timings supported */
#define STD_TIMING 8
#define STD_TIMING_DESCRIPTION_SIZE 2
#define STD_TIMING_DESCRIPTIONS_START 0x26
#define DETAILED_TIMING_DESCRIPTIONS_START 0x36
#define DETAILED_TIMING_DESCRIPTION_SIZE 18
#define NO_DETAILED_TIMING_DESCRIPTIONS 4
#define DETAILED_TIMING_DESCRIPTION_1 0x36
#define DETAILED_TIMING_DESCRIPTION_2 0x48
#define DETAILED_TIMING_DESCRIPTION_3 0x5a
#define DETAILED_TIMING_DESCRIPTION_4 0x6c
#define DESCRIPTOR_DATA 5
#define UPPER_NIBBLE( x ) \
(((128|64|32|16) & (x)) >> 4)
#define LOWER_NIBBLE( x ) \
((1|2|4|8) & (x))
#define COMBINE_HI_8LO( hi, lo ) \
( (((unsigned)hi) << 8) | (unsigned)lo )
#define COMBINE_HI_4LO( hi, lo ) \
( (((unsigned)hi) << 4) | (unsigned)lo )
#define PIXEL_CLOCK_LO (unsigned)block[ 0 ]
#define PIXEL_CLOCK_HI (unsigned)block[ 1 ]
#define PIXEL_CLOCK (COMBINE_HI_8LO( PIXEL_CLOCK_HI,PIXEL_CLOCK_LO )*10000)
#define H_ACTIVE_LO (unsigned)block[ 2 ]
#define H_BLANKING_LO (unsigned)block[ 3 ]
#define H_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 4 ] )
#define H_ACTIVE COMBINE_HI_8LO( H_ACTIVE_HI, H_ACTIVE_LO )
#define H_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 4 ] )
#define H_BLANKING COMBINE_HI_8LO( H_BLANKING_HI, H_BLANKING_LO )
#define V_ACTIVE_LO (unsigned)block[ 5 ]
#define V_BLANKING_LO (unsigned)block[ 6 ]
#define V_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 7 ] )
#define V_ACTIVE COMBINE_HI_8LO( V_ACTIVE_HI, V_ACTIVE_LO )
#define V_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 7 ] )
#define V_BLANKING COMBINE_HI_8LO( V_BLANKING_HI, V_BLANKING_LO )
#define H_SYNC_OFFSET_LO (unsigned)block[ 8 ]
#define H_SYNC_WIDTH_LO (unsigned)block[ 9 ]
#define V_SYNC_OFFSET_LO UPPER_NIBBLE( (unsigned)block[ 10 ] )
#define V_SYNC_WIDTH_LO LOWER_NIBBLE( (unsigned)block[ 10 ] )
#define V_SYNC_WIDTH_HI ((unsigned)block[ 11 ] & (1|2))
#define V_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (4|8)) >> 2)
#define H_SYNC_WIDTH_HI (((unsigned)block[ 11 ] & (16|32)) >> 4)
#define H_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (64|128)) >> 6)
#define V_SYNC_WIDTH COMBINE_HI_4LO( V_SYNC_WIDTH_HI, V_SYNC_WIDTH_LO )
#define V_SYNC_OFFSET COMBINE_HI_4LO( V_SYNC_OFFSET_HI, V_SYNC_OFFSET_LO )
#define H_SYNC_WIDTH COMBINE_HI_8LO( H_SYNC_WIDTH_HI, H_SYNC_WIDTH_LO )
#define H_SYNC_OFFSET COMBINE_HI_8LO( H_SYNC_OFFSET_HI, H_SYNC_OFFSET_LO )
#define H_SIZE_LO (unsigned)block[ 12 ]
#define V_SIZE_LO (unsigned)block[ 13 ]
#define H_SIZE_HI UPPER_NIBBLE( (unsigned)block[ 14 ] )
#define V_SIZE_HI LOWER_NIBBLE( (unsigned)block[ 14 ] )
#define H_SIZE COMBINE_HI_8LO( H_SIZE_HI, H_SIZE_LO )
#define V_SIZE COMBINE_HI_8LO( V_SIZE_HI, V_SIZE_LO )
#define H_BORDER (unsigned)block[ 15 ]
#define V_BORDER (unsigned)block[ 16 ]
#define FLAGS (unsigned)block[ 17 ]
#define INTERLACED (FLAGS&128)
#define SYNC_TYPE (FLAGS&3<<3) /* bits 4,3 */
#define SYNC_SEPARATE (3<<3)
#define HSYNC_POSITIVE (FLAGS & 4)
#define VSYNC_POSITIVE (FLAGS & 2)
#define V_MIN_RATE block[ 5 ]
#define V_MAX_RATE block[ 6 ]
#define H_MIN_RATE block[ 7 ]
#define H_MAX_RATE block[ 8 ]
#define MAX_PIXEL_CLOCK (((int)block[ 9 ]) * 10)
#define GTF_SUPPORT block[10]
#define DPMS_ACTIVE_OFF (1 << 5)
#define DPMS_SUSPEND (1 << 6)
#define DPMS_STANDBY (1 << 7)
#endif /* __EDID_H__ */

View File

@ -49,32 +49,45 @@ static int fb_enable_set(struct param_d *param, void *priv)
return 0;
}
static int fb_setup_mode(struct device_d *dev, struct param_d *param,
const char *val)
static struct fb_videomode *fb_num_to_mode(struct fb_info *info, int num)
{
struct fb_info *info = dev->priv;
int mode, ret;
int num_modes;
num_modes = info->modes.num_modes + info->edid_modes.num_modes;
if (num >= num_modes)
return NULL;
if (num >= info->modes.num_modes)
return &info->edid_modes.modes[num - info->modes.num_modes];
return &info->modes.modes[num];
}
static int fb_setup_mode(struct fb_info *info)
{
struct device_d *dev = &info->dev;
int ret;
struct fb_videomode *mode;
if (info->enabled != 0)
return -EPERM;
if (!val)
return dev_param_set_generic(dev, param, NULL);
for (mode = 0; mode < info->num_modes; mode++) {
if (!strcmp(info->mode_list[mode].name, val))
break;
}
if (mode >= info->num_modes)
mode = fb_num_to_mode(info, info->current_mode);
if (!mode)
return -EINVAL;
info->mode = &info->mode_list[mode];
info->mode = mode;
info->xres = info->mode->xres;
info->yres = info->mode->yres;
info->line_length = 0;
ret = info->fbops->fb_activate_var(info);
if (info->fbops->fb_activate_var) {
ret = info->fbops->fb_activate_var(info);
if (ret)
return ret;
}
if (!info->line_length)
info->line_length = info->xres * (info->bits_per_pixel >> 3);
@ -85,13 +98,24 @@ static int fb_setup_mode(struct device_d *dev, struct param_d *param,
dev->resource[0].start = (resource_size_t)info->screen_base;
info->cdev.size = info->line_length * info->yres;
dev->resource[0].end = dev->resource[0].start + info->cdev.size - 1;
dev_param_set_generic(dev, param, val);
} else
info->cdev.size = 0;
return ret;
}
static int fb_set_modename(struct param_d *param, void *priv)
{
struct fb_info *info = priv;
int ret;
ret = fb_setup_mode(info);
if (ret)
return ret;
return 0;
}
static struct file_operations fb_ops = {
.read = mem_read,
.write = mem_write,
@ -100,22 +124,28 @@ static struct file_operations fb_ops = {
.ioctl = fb_ioctl,
};
static void fb_print_mode(struct fb_videomode *mode)
{
printf("%-20s %dx%d@%d\n", mode->name,
mode->xres, mode->yres, mode->refresh);
}
static void fb_print_modes(struct display_timings *modes)
{
int i;
for (i = 0; i < modes->num_modes; i++)
fb_print_mode(&modes->modes[i]);
}
static void fb_info(struct device_d *dev)
{
struct fb_info *info = dev->priv;
int i;
if (!info->num_modes)
return;
printf("available modes:\n");
for (i = 0; i < info->num_modes; i++) {
struct fb_videomode *mode = &info->mode_list[i];
printf("%-10s %dx%d@%d\n", mode->name,
mode->xres, mode->yres, mode->refresh);
}
fb_print_modes(&info->modes);
fb_print_modes(&info->edid_modes);
printf("\n");
}
@ -124,10 +154,20 @@ int register_framebuffer(struct fb_info *info)
{
int id = get_free_deviceid("fb");
struct device_d *dev;
int ret;
int ret, num_modes, i;
const char **names;
dev = &info->dev;
/*
* If info->mode is set at this point it's the only mode
* the fb supports. move it over to the modes list.
*/
if (info->mode) {
info->modes.modes = info->mode;
info->modes.num_modes = 1;
}
if (!info->line_length)
info->line_length = info->xres * (info->bits_per_pixel >> 3);
@ -155,11 +195,22 @@ int register_framebuffer(struct fb_info *info)
dev_add_param_bool(dev, "enable", fb_enable_set, NULL,
&info->p_enable, info);
if (info->num_modes && (info->mode_list != NULL) &&
(info->fbops->fb_activate_var != NULL)) {
dev_add_param(dev, "mode_name", fb_setup_mode, NULL, 0);
dev_set_param(dev, "mode_name", info->mode_list[0].name);
}
if (IS_ENABLED(CONFIG_DRIVER_VIDEO_EDID))
fb_edid_add_modes(info);
num_modes = info->modes.num_modes + info->edid_modes.num_modes;
names = xzalloc(sizeof(char *) * num_modes);
for (i = 0; i < info->modes.num_modes; i++)
names[i] = info->modes.modes[i].name;
for (i = 0; i < info->edid_modes.num_modes; i++)
names[i + info->modes.num_modes] = info->edid_modes.modes[i].name;
dev_add_param_enum(dev, "mode_name", fb_set_modename, NULL, &info->current_mode, names, num_modes, info);
info->mode = fb_num_to_mode(info, 0);
fb_setup_mode(info);
ret = devfs_create(&info->cdev);
if (ret)

View File

@ -1007,8 +1007,8 @@ static int imxfb_probe(struct device_d *dev)
fbi->disable_fractional_divider = pdata->disable_fractional_divider;
info->priv = fbi;
info->fbops = &imxfb_ops;
info->num_modes = pdata->num_modes;
info->mode_list = pdata->mode;
info->modes.modes = pdata->mode;
info->modes.num_modes = pdata->num_modes;
imxfb_init_info(info, pdata->mode, pdata->bpp);

View File

@ -0,0 +1,14 @@
config DRIVER_VIDEO_IMX_IPUV3
bool "i.MX IPUv3 driver"
help
Support the IPUv3 found on Freescale i.MX51/53/6 SoCs
if DRIVER_VIDEO_IMX_IPUV3
config DRIVER_VIDEO_IMX_IPUV3_LVDS
bool "IPUv3 LVDS support"
config DRIVER_VIDEO_IMX_IPUV3_HDMI
bool "IPUv3 HDMI support"
endif

View File

@ -0,0 +1,5 @@
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += ipu-common.o ipu-dmfc.o ipu-di.o
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += ipu-dp.o ipuv3-plane.o ipufb.o
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += ipu-dc.o
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3_LVDS) += imx-ldb.o
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3_HDMI) += imx-hdmi.o

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,344 @@
/*
* Copyright 2005-2009 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
*/
#ifndef __DRM_IPU_H__
#define __DRM_IPU_H__
#include <io.h>
#include <fb.h>
#include <video/fourcc.h>
struct ipu_soc;
enum ipuv3_type {
IPUV3EX,
IPUV3M,
IPUV3H,
};
void ipuwritel(const char *unit, uint32_t value, void __iomem *reg);
uint32_t ipureadl(void __iomem *reg);
/*
* Bitfield of Display Interface signal polarities.
*/
struct ipu_di_signal_cfg {
unsigned datamask_en: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;
u16 width;
u16 height;
u32 pixel_fmt;
u16 h_start_width;
u16 h_sync_width;
u16 h_end_width;
u16 v_start_width;
u16 v_sync_width;
u16 v_end_width;
u32 v_to_h_sync;
unsigned long pixelclock;
#define IPU_DI_CLKMODE_SYNC (1 << 0)
#define IPU_DI_CLKMODE_EXT (1 << 1)
#define IPU_DI_CLKMODE_NON_FRACTIONAL (1 << 2)
unsigned long clkflags;
u8 hsync_pin;
u8 vsync_pin;
};
enum ipu_color_space {
IPUV3_COLORSPACE_RGB,
IPUV3_COLORSPACE_YUV,
IPUV3_COLORSPACE_UNKNOWN,
};
struct ipuv3_channel;
enum ipu_channel_irq {
IPU_IRQ_EOF = 0,
IPU_IRQ_NFACK = 64,
IPU_IRQ_NFB4EOF = 128,
IPU_IRQ_EOS = 192,
};
int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
enum ipu_channel_irq irq);
#define IPU_IRQ_DP_SF_START (448 + 2)
#define IPU_IRQ_DP_SF_END (448 + 3)
#define IPU_IRQ_BG_SF_END IPU_IRQ_DP_SF_END,
#define IPU_IRQ_DC_FC_0 (448 + 8)
#define IPU_IRQ_DC_FC_1 (448 + 9)
#define IPU_IRQ_DC_FC_2 (448 + 10)
#define IPU_IRQ_DC_FC_3 (448 + 11)
#define IPU_IRQ_DC_FC_4 (448 + 12)
#define IPU_IRQ_DC_FC_6 (448 + 13)
#define IPU_IRQ_VSYNC_PRE_0 (448 + 14)
#define IPU_IRQ_VSYNC_PRE_1 (448 + 15)
/*
* IPU Image DMA Controller (idmac) functions
*/
struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned channel);
void ipu_idmac_put(struct ipuv3_channel *);
int ipu_idmac_enable_channel(struct ipuv3_channel *channel);
int ipu_idmac_disable_channel(struct ipuv3_channel *channel);
int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms);
void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
bool doublebuffer);
void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
/*
* IPU Display Controller (dc) functions
*/
struct ipu_dc;
struct ipu_di;
struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel);
void ipu_dc_put(struct ipu_dc *dc);
int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
u32 pixel_fmt, u32 width);
void ipu_dc_enable_channel(struct ipu_dc *dc);
void ipu_dc_disable_channel(struct ipu_dc *dc);
/*
* IPU Display Interface (di) functions
*/
struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp);
void ipu_di_put(struct ipu_di *);
int ipu_di_disable(struct ipu_di *);
int ipu_di_enable(struct ipu_di *);
int ipu_di_get_num(struct ipu_di *);
int ipu_di_init_sync_panel(struct ipu_di *, struct ipu_di_signal_cfg *sig);
/*
* IPU Display Multi FIFO Controller (dmfc) functions
*/
struct dmfc_channel;
int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc);
void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc);
int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
unsigned long bandwidth_mbs, int burstsize);
void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc);
int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width);
struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipuv3_channel);
void ipu_dmfc_put(struct dmfc_channel *dmfc);
/*
* IPU Display Processor (dp) functions
*/
#define IPU_DP_FLOW_SYNC_BG 0
#define IPU_DP_FLOW_SYNC_FG 1
#define IPU_DP_FLOW_ASYNC0_BG 2
#define IPU_DP_FLOW_ASYNC0_FG 3
#define IPU_DP_FLOW_ASYNC1_BG 4
#define IPU_DP_FLOW_ASYNC1_FG 5
struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow);
void ipu_dp_put(struct ipu_dp *);
int ipu_dp_enable_channel(struct ipu_dp *dp);
void ipu_dp_disable_channel(struct ipu_dp *dp);
int ipu_dp_setup_channel(struct ipu_dp *dp,
enum ipu_color_space in, enum ipu_color_space out);
int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
bool bg_chan);
#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size))
#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22)
#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22)
#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4)
#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1)
#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1)
#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14)
#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14)
#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10)
#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9)
#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13)
#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12)
#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1)
#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1)
#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12)
#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11)
#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10)
#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7)
#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10)
#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1)
#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1)
#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7)
#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1)
#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1)
#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3)
#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2)
#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1)
#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3)
#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2)
#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1)
#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1)
#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1)
#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1)
#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1)
#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1)
#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13)
#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12)
#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29)
#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29)
#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20)
#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7)
#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4)
#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1)
#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3)
#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2)
#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7)
#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14)
#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3)
#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3)
#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3)
#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3)
#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5)
#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5)
#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5)
#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5)
#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1)
#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1)
#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1)
struct ipu_cpmem_word {
u32 data[5];
u32 res[3];
};
struct ipu_ch_param {
struct ipu_cpmem_word word[2];
};
void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v);
u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs);
struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel);
void ipu_ch_param_dump(struct ipu_ch_param __iomem *p);
static inline void ipu_ch_param_zero(struct ipu_ch_param __iomem *p)
{
int i;
void __iomem *base = p;
for (i = 0; i < sizeof(*p) / sizeof(u32); i++)
ipuwritel("chp", 0, base + i * sizeof(u32));
}
static inline void ipu_cpmem_set_buffer(struct ipu_ch_param __iomem *p,
int bufnum, dma_addr_t buf)
{
if (bufnum)
ipu_ch_param_write_field(p, IPU_FIELD_EBA1, buf >> 3);
else
ipu_ch_param_write_field(p, IPU_FIELD_EBA0, buf >> 3);
}
static inline void ipu_cpmem_set_resolution(struct ipu_ch_param __iomem *p,
int xres, int yres)
{
ipu_ch_param_write_field(p, IPU_FIELD_FW, xres - 1);
ipu_ch_param_write_field(p, IPU_FIELD_FH, yres - 1);
}
static inline void ipu_cpmem_set_stride(struct ipu_ch_param __iomem *p,
int stride)
{
ipu_ch_param_write_field(p, IPU_FIELD_SLY, stride - 1);
}
void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel);
struct ipu_rgb {
struct fb_bitfield red;
struct fb_bitfield green;
struct fb_bitfield blue;
struct fb_bitfield transp;
int bits_per_pixel;
};
struct ipu_rgb *drm_fourcc_to_rgb(u32 drm_fourcc);
int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p,
int width);
int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *,
const struct ipu_rgb *rgb);
static inline void ipu_cpmem_interlaced_scan(struct ipu_ch_param *p,
int stride)
{
ipu_ch_param_write_field(p, IPU_FIELD_SO, 1);
ipu_ch_param_write_field(p, IPU_FIELD_ILO, stride / 8);
ipu_ch_param_write_field(p, IPU_FIELD_SLY, (stride * 2) - 1);
};
void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format,
int stride, int height);
void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p,
u32 pixel_format);
void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
u32 pixel_format, int stride, int u_offset, int v_offset);
int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat);
enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc);
enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat);
static inline void ipu_cpmem_set_burstsize(struct ipu_ch_param __iomem *p,
int burstsize)
{
ipu_ch_param_write_field(p, IPU_FIELD_NPB, burstsize - 1);
};
struct ipu_client_platformdata {
int di;
int dc;
int dp;
int dmfc;
int dma[2];
struct device_node *device_node;
};
struct ipu_output;
struct ipu_output_ops {
int (*prepare)(struct ipu_output *ipu_video_output, struct fb_videomode *mode, int di);
int (*enable)(struct ipu_output *ipu_video_output, struct fb_videomode *mode, int di);
int (*disable)(struct ipu_output *ipu_video_output);
int (*unprepare)(struct ipu_output *ipu_video_output);
};
struct ipu_output {
struct ipu_output_ops *ops;
struct list_head list;
unsigned int di_clkflags;
uint32_t out_pixel_fmt;
struct i2c_adapter *edid_i2c_adapter;
struct display_timings *modes;
char *name;
int ipu_mask;
};
int ipu_register_output(struct ipu_output *ouput);
#endif /* __DRM_IPU_H__ */

View File

@ -0,0 +1,310 @@
/*
* i.MX drm driver - parallel display implementation
*
* Copyright (C) 2012 Sascha Hauer, Pengutronix
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <common.h>
#include <fb.h>
#include <io.h>
#include <driver.h>
#include <malloc.h>
#include <errno.h>
#include <init.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <asm-generic/div64.h>
#include <linux/clk.h>
#include <mach/imx6-regs.h>
#include <mach/imx53-regs.h>
#include "imx-ipu-v3.h"
#include "ipuv3-plane.h"
#define LDB_CH0_MODE_EN_TO_DI0 (1 << 0)
#define LDB_CH0_MODE_EN_TO_DI1 (3 << 0)
#define LDB_CH0_MODE_EN_MASK (3 << 0)
#define LDB_CH1_MODE_EN_TO_DI0 (1 << 2)
#define LDB_CH1_MODE_EN_TO_DI1 (3 << 2)
#define LDB_CH1_MODE_EN_MASK (3 << 2)
#define LDB_SPLIT_MODE_EN (1 << 4)
#define LDB_DATA_WIDTH_CH0_24 (1 << 5)
#define LDB_BIT_MAP_CH0_JEIDA (1 << 6)
#define LDB_DATA_WIDTH_CH1_24 (1 << 7)
#define LDB_BIT_MAP_CH1_JEIDA (1 << 8)
#define LDB_DI0_VS_POL_ACT_LOW (1 << 9)
#define LDB_DI1_VS_POL_ACT_LOW (1 << 10)
#define LDB_BGREF_RMODE_INT (1 << 15)
struct imx_ldb;
struct imx_ldb_channel {
struct imx_ldb *ldb;
int chno;
int mode_valid;
struct display_timings *modes;
struct ipu_output output;
};
struct imx_ldb_data {
void __iomem *base;
int (*prepare)(struct imx_ldb_channel *imx_ldb_ch, int di);
unsigned ipu_mask;
};
struct imx_ldb {
struct device_d *dev;
u32 interface_pix_fmt;
int mode_valid;
struct imx_ldb_channel channel[2];
u32 ldb_ctrl;
void __iomem *base;
const struct imx_ldb_data *soc_data;
};
enum {
LVDS_BIT_MAP_SPWG,
LVDS_BIT_MAP_JEIDA
};
static const char * const imx_ldb_bit_mappings[] = {
[LVDS_BIT_MAP_SPWG] = "spwg",
[LVDS_BIT_MAP_JEIDA] = "jeida",
};
static const 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(imx_ldb_bit_mappings); i++)
if (!strcasecmp(bm, imx_ldb_bit_mappings[i]))
return i;
return -EINVAL;
}
static int imx_ldb_prepare(struct ipu_output *output, struct fb_videomode *mode, int di)
{
struct imx_ldb_channel *imx_ldb_ch = container_of(output, struct imx_ldb_channel, output);
struct imx_ldb *ldb = imx_ldb_ch->ldb;
if (PICOS2KHZ(mode->pixclock) > 85000) {
dev_warn(ldb->dev,
"%s: mode exceeds 85 MHz pixel clock\n", __func__);
}
ldb->soc_data->prepare(imx_ldb_ch, di);
/* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */
if (imx_ldb_ch == &ldb->channel[0]) {
if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
else
ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW;
}
if (imx_ldb_ch == &ldb->channel[1]) {
if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
else
ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW;
}
if (imx_ldb_ch == &ldb->channel[0]) {
ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0;
}
if (imx_ldb_ch == &ldb->channel[1]) {
ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI1;
}
writel(ldb->ldb_ctrl, ldb->base);
return 0;
}
static int imx6q_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di)
{
struct clk *diclk, *ldbclk;
struct imx_ldb *ldb = imx_ldb_ch->ldb;
int ret, ipuno, dino;
char *clkname;
void __iomem *gpr3 = (void *)MX6_IOMUXC_BASE_ADDR + 0xc;
uint32_t val;
int shift;
ipuno = ((di >> 1) & 1) + 1;
dino = di & 0x1;
clkname = asprintf("ipu%d_di%d_sel", ipuno, dino);
diclk = clk_lookup(clkname);
free(clkname);
if (IS_ERR(diclk)) {
dev_err(ldb->dev, "failed to get di clk: %s\n", strerror(PTR_ERR(diclk)));
return PTR_ERR(diclk);
}
clkname = asprintf("ldb_di%d_podf", imx_ldb_ch->chno);
ldbclk = clk_lookup(clkname);
free(clkname);
if (IS_ERR(ldbclk)) {
dev_err(ldb->dev, "failed to get ldb clk: %s\n", strerror(PTR_ERR(ldbclk)));
return PTR_ERR(ldbclk);
}
ret = clk_set_parent(diclk, ldbclk);
if (ret) {
dev_err(ldb->dev, "failed to set display clock parent: %s\n", strerror(-ret));
return ret;
}
printk("%s: %d\n", __func__, di);
val = readl(gpr3);
shift = (imx_ldb_ch->chno == 0) ? 6 : 8;
val &= ~(3 << shift);
val |= di << shift;
writel(val, gpr3);
return 0;
}
static int imx53_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di)
{
return -ENOSYS;
}
static struct imx_ldb_data imx_ldb_data_imx6q = {
.base = (void *)MX6_IOMUXC_BASE_ADDR + 0x8,
.prepare = imx6q_ldb_prepare,
.ipu_mask = 0xf,
};
static struct imx_ldb_data imx_ldb_data_imx53 = {
.base = (void *)MX53_IOMUXC_BASE_ADDR + 0x8,
.prepare = imx53_ldb_prepare,
.ipu_mask = 0x3,
};
static struct ipu_output_ops imx_ldb_ops = {
.prepare = imx_ldb_prepare,
};
static int imx_ldb_probe(struct device_d *dev)
{
struct device_node *np = dev->device_node;
struct device_node *child;
struct imx_ldb *imx_ldb;
int ret, i;
int dual = 0;
int datawidth;
int mapping;
const struct imx_ldb_data *devtype;
ret = dev_get_drvdata(dev, (unsigned long *)&devtype);
if (ret)
return ret;
imx_ldb = xzalloc(sizeof(*imx_ldb));
imx_ldb->base = devtype->base;
imx_ldb->soc_data = devtype;
for_each_child_of_node(np, child) {
struct imx_ldb_channel *channel;
ret = of_property_read_u32(child, "reg", &i);
if (ret || i < 0 || i > 1)
return -EINVAL;
if (dual && i > 0) {
dev_warn(dev, "dual-channel mode, ignoring second output\n");
continue;
}
if (!of_device_is_available(child))
continue;
channel = &imx_ldb->channel[i];
channel->ldb = imx_ldb;
channel->chno = i;
ret = of_property_read_u32(child, "fsl,data-width", &datawidth);
if (ret)
datawidth = 0;
else if (datawidth != 18 && datawidth != 24)
return -EINVAL;
mapping = of_get_data_mapping(child);
switch (mapping) {
case LVDS_BIT_MAP_SPWG:
if (datawidth == 24) {
if (i == 0 || dual)
imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
if (i == 1 || dual)
imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
}
break;
case LVDS_BIT_MAP_JEIDA:
if (datawidth == 18) {
dev_err(dev, "JEIDA standard only supported in 24 bit\n");
return -EINVAL;
}
if (i == 0 || dual)
imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 | LDB_BIT_MAP_CH0_JEIDA;
if (i == 1 || dual)
imx_ldb->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;
}
channel->output.ops = &imx_ldb_ops;
channel->output.di_clkflags = IPU_DI_CLKMODE_EXT | IPU_DI_CLKMODE_SYNC;
channel->output.out_pixel_fmt = (datawidth == 24) ?
V4L2_PIX_FMT_RGB24 : V4L2_PIX_FMT_BGR666;
channel->output.modes = of_get_display_timings(child);
channel->output.name = asprintf("ldb-%d", i);
channel->output.ipu_mask = devtype->ipu_mask;
ipu_register_output(&channel->output);
}
return 0;
}
static struct of_device_id imx_ldb_dt_ids[] = {
{ .compatible = "fsl,imx6q-ldb", (unsigned long)&imx_ldb_data_imx6q},
{ .compatible = "fsl,imx53-ldb", (unsigned long)&imx_ldb_data_imx53},
{ /* sentinel */ }
};
static struct driver_d imx_ldb_driver = {
.probe = imx_ldb_probe,
.of_compatible = imx_ldb_dt_ids,
.name = "imx-ldb",
};
device_platform_driver(imx_ldb_driver);
MODULE_DESCRIPTION("i.MX LVDS display driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,836 @@
/*
* Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
* Copyright (C) 2005-2009 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.
*/
#include <common.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <clock.h>
#include <driver.h>
#include <init.h>
#include <asm/mmu.h>
#include <mach/generic.h>
#include <mach/imx6-regs.h>
#include "imx-ipu-v3.h"
#include "ipu-prv.h"
void ipuwritel(const char *unit, uint32_t value, void __iomem *reg)
{
pr_debug("w: %s 0x%08lx -> 0x%08x\n", unit, (unsigned long)reg & 0xfff, value);
writel(value, reg);
}
uint32_t ipureadl(void __iomem *reg)
{
uint32_t val;
val = readl(reg);
pr_debug("r: 0x%p -> 0x%08x\n", reg - 0x02600000, val);
return val;
}
static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
{
return ipureadl(ipu->cm_reg + offset);
}
static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
{
ipuwritel("cm", value, ipu->cm_reg + offset);
}
static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
{
return ipureadl(ipu->idmac_reg + offset);
}
static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value,
unsigned offset)
{
ipuwritel("idmac", value, ipu->idmac_reg + offset);
}
void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
{
u32 val;
val = ipu_cm_read(ipu, IPU_SRM_PRI2);
val |= 0x8;
ipu_cm_write(ipu, val, IPU_SRM_PRI2);
}
EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update);
struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
return ipu->cpmem_base + channel->num;
}
EXPORT_SYMBOL_GPL(ipu_get_cpmem);
void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel);
u32 val;
if (ipu->ipu_type == IPUV3EX)
ipu_ch_param_write_field(p, IPU_FIELD_ID, 1);
val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num));
val |= 1 << (channel->num % 32);
ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num));
};
EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority);
void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v)
{
u32 bit = (wbs >> 8) % 160;
u32 size = wbs & 0xff;
u32 word = (wbs >> 8) / 160;
u32 i = bit / 32;
u32 ofs = bit % 32;
u32 mask = (1 << size) - 1;
u32 val;
pr_debug("%s %d %d %d 0x%08x\n", __func__, word, bit , size, v);
val = ipureadl(&base->word[word].data[i]);
val &= ~(mask << ofs);
val |= v << ofs;
ipuwritel("chp", val, &base->word[word].data[i]);
if ((bit + size - 1) / 32 > i) {
val = ipureadl(&base->word[word].data[i + 1]);
val &= ~(mask >> (ofs ? (32 - ofs) : 0));
val |= v >> (ofs ? (32 - ofs) : 0);
ipuwritel("chp", val, &base->word[word].data[i + 1]);
}
}
EXPORT_SYMBOL_GPL(ipu_ch_param_write_field);
u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs)
{
u32 bit = (wbs >> 8) % 160;
u32 size = wbs & 0xff;
u32 word = (wbs >> 8) / 160;
u32 i = bit / 32;
u32 ofs = bit % 32;
u32 mask = (1 << size) - 1;
u32 val = 0;
pr_debug("%s %d %d %d\n", __func__, word, bit , size);
val = (ipureadl(&base->word[word].data[i]) >> ofs) & mask;
if ((bit + size - 1) / 32 > i) {
u32 tmp;
tmp = ipureadl(&base->word[word].data[i + 1]);
tmp &= mask >> (ofs ? (32 - ofs) : 0);
val |= tmp << (ofs ? (32 - ofs) : 0);
}
return val;
}
EXPORT_SYMBOL_GPL(ipu_ch_param_read_field);
int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p,
const struct ipu_rgb *rgb)
{
int bpp = 0, npb = 0, ro, go, bo, to;
ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset;
go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset;
bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset;
to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset;
ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1);
ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro);
ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1);
ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go);
ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1);
ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo);
if (rgb->transp.length) {
ipu_ch_param_write_field(p, IPU_FIELD_WID3,
rgb->transp.length - 1);
ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to);
} else {
ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7);
ipu_ch_param_write_field(p, IPU_FIELD_OFS3,
rgb->bits_per_pixel);
}
switch (rgb->bits_per_pixel) {
case 32:
bpp = 0;
npb = 15;
break;
case 24:
bpp = 1;
npb = 19;
break;
case 16:
bpp = 3;
npb = 31;
break;
case 8:
bpp = 5;
npb = 63;
break;
default:
return -EINVAL;
}
ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */
return 0;
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb);
static struct ipu_rgb def_rgb_32 = {
.red = { .offset = 16, .length = 8, },
.green = { .offset = 8, .length = 8, },
.blue = { .offset = 0, .length = 8, },
.transp = { .offset = 24, .length = 8, },
.bits_per_pixel = 32,
};
static struct ipu_rgb def_bgr_32 = {
.red = { .offset = 0, .length = 8, },
.green = { .offset = 8, .length = 8, },
.blue = { .offset = 16, .length = 8, },
.transp = { .offset = 24, .length = 8, },
.bits_per_pixel = 32,
};
static struct ipu_rgb def_rgb_24 = {
.red = { .offset = 16, .length = 8, },
.green = { .offset = 8, .length = 8, },
.blue = { .offset = 0, .length = 8, },
.transp = { .offset = 0, .length = 0, },
.bits_per_pixel = 24,
};
static struct ipu_rgb def_bgr_24 = {
.red = { .offset = 0, .length = 8, },
.green = { .offset = 8, .length = 8, },
.blue = { .offset = 16, .length = 8, },
.transp = { .offset = 0, .length = 0, },
.bits_per_pixel = 24,
};
static struct ipu_rgb def_rgb_16 = {
.red = { .offset = 11, .length = 5, },
.green = { .offset = 5, .length = 6, },
.blue = { .offset = 0, .length = 5, },
.transp = { .offset = 0, .length = 0, },
.bits_per_pixel = 16,
};
static struct ipu_rgb def_bgr_16 = {
.red = { .offset = 0, .length = 5, },
.green = { .offset = 5, .length = 6, },
.blue = { .offset = 11, .length = 5, },
.transp = { .offset = 0, .length = 0, },
.bits_per_pixel = 16,
};
struct ipu_rgb *drm_fourcc_to_rgb(u32 drm_fourcc)
{
switch (drm_fourcc) {
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_XBGR8888:
return &def_bgr_32;
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_XRGB8888:
return &def_rgb_32;
case DRM_FORMAT_BGR888:
return &def_bgr_24;
case DRM_FORMAT_RGB888:
return &def_rgb_24;
case DRM_FORMAT_RGB565:
return &def_rgb_16;
case DRM_FORMAT_BGR565:
return &def_bgr_16;
default:
return NULL;
}
}
#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y))
#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \
(pix->width * (y) / 4) + (x) / 2)
#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \
(pix->width * pix->height / 4) + \
(pix->width * (y) / 4) + (x) / 2)
int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc)
{
switch (drm_fourcc) {
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_XBGR8888:
ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32);
break;
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_XRGB8888:
ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32);
break;
case DRM_FORMAT_BGR888:
ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24);
break;
case DRM_FORMAT_RGB888:
ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24);
break;
case DRM_FORMAT_RGB565:
ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16);
break;
case DRM_FORMAT_BGR565:
ipu_cpmem_set_format_rgb(cpmem, &def_bgr_16);
break;
default:
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
{
struct ipuv3_channel *channel;
dev_dbg(ipu->dev, "%s %d\n", __func__, num);
if (num > 63)
return ERR_PTR(-ENODEV);
mutex_lock(&ipu->channel_lock);
channel = &ipu->channel[num];
if (channel->busy) {
channel = ERR_PTR(-EBUSY);
goto out;
}
channel->busy = true;
channel->num = num;
out:
mutex_unlock(&ipu->channel_lock);
return channel;
}
EXPORT_SYMBOL_GPL(ipu_idmac_get);
void ipu_idmac_put(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
mutex_lock(&ipu->channel_lock);
channel->busy = false;
mutex_unlock(&ipu->channel_lock);
}
EXPORT_SYMBOL_GPL(ipu_idmac_put);
#define idma_mask(ch) (1 << (ch & 0x1f))
void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
bool doublebuffer)
{
struct ipu_soc *ipu = channel->ipu;
u32 reg;
reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
if (doublebuffer)
reg |= idma_mask(channel->num);
else
reg &= ~idma_mask(channel->num);
ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
}
EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
{
u32 val;
val = ipu_cm_read(ipu, IPU_DISP_GEN);
if (mask & IPU_CONF_DI0_EN)
val |= IPU_DI0_COUNTER_RELEASE;
if (mask & IPU_CONF_DI1_EN)
val |= IPU_DI1_COUNTER_RELEASE;
ipu_cm_write(ipu, val, IPU_DISP_GEN);
val = ipu_cm_read(ipu, IPU_CONF);
val |= mask;
ipu_cm_write(ipu, val, IPU_CONF);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_module_enable);
int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
{
u32 val;
val = ipu_cm_read(ipu, IPU_CONF);
val &= ~mask;
ipu_cm_write(ipu, val, IPU_CONF);
val = ipu_cm_read(ipu, IPU_DISP_GEN);
if (mask & IPU_CONF_DI0_EN)
val &= ~IPU_DI0_COUNTER_RELEASE;
if (mask & IPU_CONF_DI1_EN)
val &= ~IPU_DI1_COUNTER_RELEASE;
ipu_cm_write(ipu, val, IPU_DISP_GEN);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_module_disable);
void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
{
struct ipu_soc *ipu = channel->ipu;
unsigned int chno = channel->num;
/* Mark buffer as ready. */
if (buf_num == 0)
ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
else
ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
}
EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
u32 val;
val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
val |= idma_mask(channel->num);
ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
return 0;
}
EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
{
struct ipu_soc *ipu = channel->ipu;
uint64_t start;
start = get_time_ns();
while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
idma_mask(channel->num)) {
if (is_timeout(start, ms * MSECOND))
return -ETIMEDOUT;
}
return 0;
}
EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
u32 val;
/* Disable DMA channel(s) */
val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
val &= ~idma_mask(channel->num);
ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
/* Set channel buffers NOT to be ready */
ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
idma_mask(channel->num)) {
ipu_cm_write(ipu, idma_mask(channel->num),
IPU_CHA_BUF0_RDY(channel->num));
}
if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
idma_mask(channel->num)) {
ipu_cm_write(ipu, idma_mask(channel->num),
IPU_CHA_BUF1_RDY(channel->num));
}
ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
/* Reset the double buffer */
val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
val &= ~idma_mask(channel->num);
ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
return 0;
}
EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
static int ipu_memory_reset(struct ipu_soc *ipu)
{
uint64_t start;
ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
start = get_time_ns();
while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
if (is_timeout(start, SECOND))
return -ETIMEDOUT;
}
return 0;
}
static int imx6_ipu_reset(struct ipu_soc *ipu)
{
uint32_t val;
int ret;
void __iomem *reg;
reg = (void *)MX6_SRC_BASE_ADDR;
val = ipureadl(reg);
if (ipu->base == (void *)MX6_IPU1_BASE_ADDR)
val |= (1 << 3);
else
val |= (1 << 12);
ipuwritel("reset", val, reg);
ret = wait_on_timeout(100 * MSECOND, !(readl(reg) & (1 << 3)));
return ret;
}
struct ipu_devtype {
const char *name;
unsigned long cm_ofs;
unsigned long cpmem_ofs;
unsigned long srm_ofs;
unsigned long tpm_ofs;
unsigned long disp0_ofs;
unsigned long disp1_ofs;
unsigned long dc_tmpl_ofs;
unsigned long vdi_ofs;
enum ipuv3_type type;
int (*reset)(struct ipu_soc *ipu);
};
static struct ipu_devtype ipu_type_imx51 = {
.name = "IPUv3EX",
.cm_ofs = 0x1e000000,
.cpmem_ofs = 0x1f000000,
.srm_ofs = 0x1f040000,
.tpm_ofs = 0x1f060000,
.disp0_ofs = 0x1e040000,
.disp1_ofs = 0x1e048000,
.dc_tmpl_ofs = 0x1f080000,
.vdi_ofs = 0x1e068000,
.type = IPUV3EX,
};
static struct ipu_devtype ipu_type_imx53 = {
.name = "IPUv3M",
.cm_ofs = 0x06000000,
.cpmem_ofs = 0x07000000,
.srm_ofs = 0x07040000,
.tpm_ofs = 0x07060000,
.disp0_ofs = 0x06040000,
.disp1_ofs = 0x06048000,
.dc_tmpl_ofs = 0x07080000,
.vdi_ofs = 0x06068000,
.type = IPUV3M,
};
static struct ipu_devtype ipu_type_imx6q = {
.name = "IPUv3H",
.cm_ofs = 0x00200000,
.cpmem_ofs = 0x00300000,
.srm_ofs = 0x00340000,
.tpm_ofs = 0x00360000,
.disp0_ofs = 0x00240000,
.disp1_ofs = 0x00248000,
.dc_tmpl_ofs = 0x00380000,
.vdi_ofs = 0x00268000,
.type = IPUV3H,
.reset = imx6_ipu_reset,
};
static struct of_device_id imx_ipu_dt_ids[] = {
{ .compatible = "fsl,imx51-ipu", .data = (unsigned long)&ipu_type_imx51, },
{ .compatible = "fsl,imx53-ipu", .data = (unsigned long)&ipu_type_imx53, },
{ .compatible = "fsl,imx6q-ipu", .data = (unsigned long)&ipu_type_imx6q, },
{ /* sentinel */ }
};
static int ipu_submodules_init(struct ipu_soc *ipu,
struct device_d *dev, void __iomem *ipu_base,
struct clk *ipu_clk)
{
char *unit;
int ret;
const struct ipu_devtype *devtype = ipu->devtype;
ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
IPU_CONF_DI0_EN, ipu_clk);
if (ret) {
unit = "di0";
goto err_di_0;
}
ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
IPU_CONF_DI1_EN, ipu_clk);
if (ret) {
unit = "di1";
goto err_di_1;
}
ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
if (ret) {
unit = "dc_template";
goto err_dc;
}
ret = ipu_dmfc_init(ipu, dev, ipu_base +
devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
if (ret) {
unit = "dmfc";
goto err_dmfc;
}
ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
if (ret) {
unit = "dp";
goto err_dp;
}
return 0;
err_dp:
ipu_dmfc_exit(ipu);
err_dmfc:
ipu_dc_exit(ipu);
err_dc:
ipu_di_exit(ipu, 1);
err_di_1:
ipu_di_exit(ipu, 0);
err_di_0:
dev_err(dev, "init %s failed with %d\n", unit, ret);
return ret;
}
static void ipu_submodules_exit(struct ipu_soc *ipu)
{
ipu_dp_exit(ipu);
ipu_dmfc_exit(ipu);
ipu_dc_exit(ipu);
ipu_di_exit(ipu, 1);
ipu_di_exit(ipu, 0);
}
struct ipu_platform_reg {
struct ipu_client_platformdata pdata;
const char *name;
};
static struct ipu_platform_reg client_reg[] = {
{
.pdata = {
.di = 0,
.dc = 5,
.dp = IPU_DP_FLOW_SYNC_BG,
.dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
.dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
},
.name = "imx-ipuv3-crtc",
}, {
.pdata = {
.di = 1,
.dc = 1,
.dp = -EINVAL,
.dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
.dma[1] = -EINVAL,
},
.name = "imx-ipuv3-crtc",
},
};
static int ipu_client_id;
static int ipu_add_subdevice_pdata(struct device_d *ipu_dev,
struct ipu_platform_reg *reg)
{
struct device_d *dev;
int ret;
dev = device_alloc(reg->name, ipu_client_id++);
dev->parent = ipu_dev;
device_add_data(dev, &reg->pdata, sizeof(reg->pdata));
((struct ipu_client_platformdata *)dev->platform_data)->device_node = ipu_dev->device_node;
ret = platform_device_register(dev);
return ret;
}
static int ipu_add_client_devices(struct ipu_soc *ipu)
{
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
struct ipu_platform_reg *reg = &client_reg[i];
ret = ipu_add_subdevice_pdata(ipu->dev, reg);
if (ret)
goto err_register;
}
return 0;
err_register:
return ret;
}
static int ipu_probe(struct device_d *dev)
{
struct ipu_soc *ipu;
void __iomem *ipu_base;
int i, ret;
const struct ipu_devtype *devtype;
ret = dev_get_drvdata(dev, (unsigned long *)&devtype);
if (ret)
return ret;
ipu_base = dev_request_mem_region(dev, 0);
if (!ipu_base)
return -EBUSY;
ipu = xzalloc(sizeof(*ipu));
ipu->base = ipu_base;
for (i = 0; i < 64; i++)
ipu->channel[i].ipu = ipu;
ipu->devtype = devtype;
ipu->ipu_type = devtype->type;
dev_dbg(dev, "cm_reg: 0x%p\n",
ipu_base + devtype->cm_ofs);
dev_dbg(dev, "idmac: 0x%p\n",
ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
dev_dbg(dev, "cpmem: 0x%p\n",
ipu_base + devtype->cpmem_ofs);
dev_dbg(dev, "disp0: 0x%p\n",
ipu_base + devtype->disp0_ofs);
dev_dbg(dev, "disp1: 0x%p\n",
ipu_base + devtype->disp1_ofs);
dev_dbg(dev, "srm: 0x%p\n",
ipu_base + devtype->srm_ofs);
dev_dbg(dev, "tpm: 0x%p\n",
ipu_base + devtype->tpm_ofs);
dev_dbg(dev, "dc: 0x%p\n",
ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
dev_dbg(dev, "ic: 0x%p\n",
ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
dev_dbg(dev, "dmfc: 0x%p\n",
ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
dev_dbg(dev, "vdi: 0x%p\n",
ipu_base + devtype->vdi_ofs);
ipu->cm_reg = ipu_base + devtype->cm_ofs, PAGE_SIZE;
ipu->idmac_reg = ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS;
ipu->cpmem_base = ipu_base + devtype->cpmem_ofs;
ipu->clk = clk_get(dev, "bus");
if (IS_ERR(ipu->clk)) {
ret = PTR_ERR(ipu->clk);
dev_err(dev, "clk_get failed with %d", ret);
return ret;
}
dev->priv = ipu;
ret = clk_enable(ipu->clk);
if (ret)
return ret;
ipu->dev = dev;
ret = devtype->reset(ipu);
if (ret) {
dev_err(dev, "failed to reset: %d\n", ret);
goto out_failed_reset;
}
ret = ipu_memory_reset(ipu);
if (ret)
goto out_failed_reset;
/* Set MCU_T to divide MCU access window into 2 */
ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
IPU_DISP_GEN);
ret = ipu_submodules_init(ipu, dev, ipu_base, ipu->clk);
if (ret)
goto failed_submodules_init;
ret = ipu_add_client_devices(ipu);
if (ret) {
dev_err(dev, "adding client devices failed with %d\n",
ret);
goto failed_add_clients;
}
dev_info(dev, "%s probed\n", devtype->name);
return 0;
failed_add_clients:
ipu_submodules_exit(ipu);
failed_submodules_init:
out_failed_reset:
clk_disable(ipu->clk);
return ret;
}
static struct driver_d imx_ipu_driver = {
.name = "imx-ipuv3",
.of_compatible = imx_ipu_dt_ids,
.probe = ipu_probe,
};
device_platform_driver(imx_ipu_driver);
MODULE_DESCRIPTION("i.MX IPU v3 driver");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,392 @@
/*
* Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
* Copyright (C) 2005-2009 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.
*/
#include <common.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <malloc.h>
#include "imx-ipu-v3.h"
#include "ipu-prv.h"
#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2)
#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~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_WR_CH_CONF 0x0
#define DC_WR_CH_ADDR 0x4
#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2)
#define DC_GEN 0xd4
#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4)
#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4)
#define DC_STAT 0x1c8
#define WROD(lf) (0x18 | ((lf) << 1))
#define WRG 0x01
#define WCLK 0xc9
#define SYNC_WAVE 0
#define NULL_WAVE (-1)
#define DC_GEN_SYNC_1_6_SYNC (2 << 1)
#define DC_GEN_SYNC_PRIORITY_1 (1 << 7)
#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0)
#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0)
#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0)
#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0)
#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3)
#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3)
#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4)
#define DC_WR_CH_CONF_FIELD_MODE (1 << 9)
#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5)
#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5)
#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2)
#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3)
#define IPU_DC_NUM_CHANNELS 10
struct ipu_dc_priv;
enum ipu_dc_map {
IPU_DC_MAP_RGB24,
IPU_DC_MAP_RGB565,
IPU_DC_MAP_GBR24, /* TVEv2 */
IPU_DC_MAP_BGR666,
IPU_DC_MAP_BGR24,
};
struct ipu_dc {
/* The display interface number assigned to this dc channel */
unsigned int di;
void __iomem *base;
struct ipu_dc_priv *priv;
int chno;
bool in_use;
};
struct ipu_dc_priv {
void __iomem *dc_reg;
void __iomem *dc_tmpl_reg;
struct ipu_soc *ipu;
struct device_d *dev;
struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
};
static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
{
u32 reg;
reg = ipureadl(dc->base + DC_RL_CH(event));
reg &= ~(0xffff << (16 * (event & 0x1)));
reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
ipuwritel("dc", reg, dc->base + DC_RL_CH(event));
}
static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand,
int map, int wave, int glue, int sync, int stop)
{
struct ipu_dc_priv *priv = dc->priv;
u32 reg1, reg2;
if (opcode == WCLK) {
reg1 = (operand << 20) & 0xfff00000;
reg2 = operand >> 12 | opcode << 1 | stop << 9;
} else if (opcode == WRG) {
reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000);
reg2 = operand >> 17 | opcode << 7 | stop << 9;
} else {
reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000);
reg2 = operand >> 12 | opcode << 4 | stop << 9;
}
ipuwritel("dc", reg1, priv->dc_tmpl_reg + word * 8);
ipuwritel("dc", reg2, priv->dc_tmpl_reg + word * 8 + 4);
}
static int ipu_pixfmt_to_map(u32 fmt)
{
switch (fmt) {
case V4L2_PIX_FMT_RGB24:
return IPU_DC_MAP_RGB24;
case V4L2_PIX_FMT_RGB565:
return IPU_DC_MAP_RGB565;
case IPU_PIX_FMT_GBR24:
return IPU_DC_MAP_GBR24;
case V4L2_PIX_FMT_BGR666:
return IPU_DC_MAP_BGR666;
case V4L2_PIX_FMT_BGR24:
return IPU_DC_MAP_BGR24;
default:
return -EINVAL;
}
}
int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
u32 pixel_fmt, u32 width)
{
struct ipu_dc_priv *priv = dc->priv;
u32 reg = 0;
int map;
dc->di = ipu_di_get_num(di);
map = ipu_pixfmt_to_map(pixel_fmt);
if (map < 0) {
dev_dbg(priv->dev, "IPU_DISP: No MAP\n");
return map;
}
if (interlaced) {
dc_link_event(dc, DC_EVT_NL, 0, 3);
dc_link_event(dc, DC_EVT_EOL, 0, 2);
dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
/* Init template microcode */
dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
} else {
if (dc->di) {
dc_link_event(dc, DC_EVT_NL, 2, 3);
dc_link_event(dc, DC_EVT_EOL, 3, 2);
dc_link_event(dc, DC_EVT_NEW_DATA, 1, 1);
/* Init template microcode */
dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0);
dc_write_tmpl(dc, 4, WRG, 0, map, NULL_WAVE, 0, 0, 1);
dc_write_tmpl(dc, 1, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
} else {
dc_link_event(dc, DC_EVT_NL, 5, 3);
dc_link_event(dc, DC_EVT_EOL, 6, 2);
dc_link_event(dc, DC_EVT_NEW_DATA, 8, 1);
/* Init template microcode */
dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0);
dc_write_tmpl(dc, 7, WRG, 0, map, NULL_WAVE, 0, 0, 1);
dc_write_tmpl(dc, 8, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
}
}
dc_link_event(dc, DC_EVT_NF, 0, 0);
dc_link_event(dc, DC_EVT_NFIELD, 0, 0);
dc_link_event(dc, DC_EVT_EOF, 0, 0);
dc_link_event(dc, DC_EVT_EOFIELD, 0, 0);
dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0);
dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0);
reg = ipureadl(dc->base + DC_WR_CH_CONF);
if (interlaced)
reg |= DC_WR_CH_CONF_FIELD_MODE;
else
reg &= ~DC_WR_CH_CONF_FIELD_MODE;
ipuwritel("dc", reg, dc->base + DC_WR_CH_CONF);
ipuwritel("dc", 0x0, dc->base + DC_WR_CH_ADDR);
ipuwritel("dc", width, priv->dc_reg + DC_DISP_CONF2(dc->di));
ipu_module_enable(priv->ipu, IPU_CONF_DC_EN);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
void ipu_dc_enable_channel(struct ipu_dc *dc)
{
int di;
u32 reg;
di = dc->di;
reg = ipureadl(dc->base + DC_WR_CH_CONF);
reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL;
ipuwritel("dc", reg, dc->base + DC_WR_CH_CONF);
}
EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
void ipu_dc_disable_channel(struct ipu_dc *dc)
{
struct ipu_dc_priv *priv = dc->priv;
u32 val;
int irq = 0, timeout = 50;
if (dc->chno == 1)
irq = IPU_IRQ_DC_FC_1;
else if (dc->chno == 5)
irq = IPU_IRQ_DP_SF_END;
else
return;
/* should wait for the interrupt here */
mdelay(50);
if (dc->di == 0)
val = 0x00000002;
else
val = 0x00000020;
/* Wait for DC triple buffer to empty */
while ((ipureadl(priv->dc_reg + DC_STAT) & val) != val) {
mdelay(2);
timeout -= 2;
if (timeout <= 0)
break;
}
val = ipureadl(dc->base + DC_WR_CH_CONF);
val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
ipuwritel("dc", val, dc->base + DC_WR_CH_CONF);
}
EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
int byte_num, int offset, int mask)
{
int ptr = map * 3 + byte_num;
u32 reg;
reg = ipureadl(priv->dc_reg + DC_MAP_CONF_VAL(ptr));
reg &= ~(0xffff << (16 * (ptr & 0x1)));
reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
ipuwritel("dc", reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr));
reg = ipureadl(priv->dc_reg + DC_MAP_CONF_PTR(map));
reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
ipuwritel("dc", reg, priv->dc_reg + DC_MAP_CONF_PTR(map));
}
static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map)
{
u32 reg = ipureadl(priv->dc_reg + DC_MAP_CONF_PTR(map));
ipuwritel("dc", reg & ~(0xffff << (16 * (map & 0x1))),
priv->dc_reg + DC_MAP_CONF_PTR(map));
}
struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel)
{
struct ipu_dc_priv *priv = ipu->dc_priv;
struct ipu_dc *dc;
if (channel >= IPU_DC_NUM_CHANNELS)
return ERR_PTR(-ENODEV);
dc = &priv->channels[channel];
if (dc->in_use)
return ERR_PTR(-EBUSY);
dc->in_use = true;
return dc;
}
EXPORT_SYMBOL_GPL(ipu_dc_get);
void ipu_dc_put(struct ipu_dc *dc)
{
dc->in_use = false;
}
EXPORT_SYMBOL_GPL(ipu_dc_put);
int ipu_dc_init(struct ipu_soc *ipu, struct device_d *dev,
void __iomem *base, void __iomem *template_base)
{
struct ipu_dc_priv *priv;
static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
0x78, 0, 0x94, 0xb4};
int i;
priv = xzalloc(sizeof(*priv));
priv->dev = dev;
priv->ipu = ipu;
priv->dc_reg = base;
priv->dc_tmpl_reg = template_base;
for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) {
priv->channels[i].chno = i;
priv->channels[i].priv = priv;
priv->channels[i].base = priv->dc_reg + channel_offsets[i];
}
ipuwritel("dc", DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
DC_WR_CH_CONF_PROG_DI_ID,
priv->channels[1].base + DC_WR_CH_CONF);
ipuwritel("dc", DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0),
priv->channels[5].base + DC_WR_CH_CONF);
ipuwritel("dc", DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, priv->dc_reg + DC_GEN);
ipu->dc_priv = priv;
dev_dbg(dev, "DC base: 0x%p template base: 0x%p\n",
base, template_base);
/* rgb24 */
ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24);
ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */
ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */
ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */
/* rgb565 */
ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565);
ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */
ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */
ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */
/* gbr24 */
ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24);
ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */
ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */
ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */
/* bgr666 */
ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666);
ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */
ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */
ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */
/* bgr24 */
ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24);
ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */
ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */
ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */
return 0;
}
void ipu_dc_exit(struct ipu_soc *ipu)
{
}

View File

@ -0,0 +1,762 @@
/*
* Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
* Copyright (C) 2005-2009 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.
*/
#include <common.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <asm-generic/div64.h>
#include <malloc.h>
#include "imx-ipu-v3.h"
#include "ipu-prv.h"
struct ipu_di {
void __iomem *base;
int id;
u32 module;
struct clk *clk_di; /* display input clock */
struct clk *clk_ipu; /* IPU bus clock */
struct clk clk_di_pixel; /* resulting pixel clock */
char *clk_name;
const char *di_parent_names[2];
bool inuse;
unsigned long clkflags;
struct ipu_soc *ipu;
};
struct di_sync_config {
int run_count;
int run_src;
int offset_count;
int offset_src;
int repeat_count;
int cnt_clr_src;
int cnt_polarity_gen_en;
int cnt_polarity_clr_src;
int cnt_polarity_trigger_src;
int cnt_up;
int cnt_down;
};
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 = 0,
DI_SYNC_CLK = 1,
DI_SYNC_INT_HSYNC = 2,
DI_SYNC_HSYNC = 3,
DI_SYNC_VSYNC = 4,
DI_SYNC_DE = 6,
};
#define SYNC_WAVE 0
#define DI_GENERAL 0x0000
#define DI_BS_CLKGEN0 0x0004
#define DI_BS_CLKGEN1 0x0008
#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1))
#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1))
#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2))
#define DI_SYNC_AS_GEN 0x0054
#define DI_DW_GEN(gen) (0x0058 + 4 * (gen))
#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set)))
#define DI_SER_CONF 0x015c
#define DI_SSC 0x0160
#define DI_POL 0x0164
#define DI_AW0 0x0168
#define DI_AW1 0x016c
#define DI_SCR_CONF 0x0170
#define DI_STAT 0x0174
#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19)
#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16)
#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3)
#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0)
#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29)
#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25)
#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12)
#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9)
#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16)
#define DI_SW_GEN1_CNT_UP(x) (x)
#define DI_SW_GEN1_AUTO_RELOAD (0x10000000)
#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24
#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16
#define DI_GEN_POLARITY_1 (1 << 0)
#define DI_GEN_POLARITY_2 (1 << 1)
#define DI_GEN_POLARITY_3 (1 << 2)
#define DI_GEN_POLARITY_4 (1 << 3)
#define DI_GEN_POLARITY_5 (1 << 4)
#define DI_GEN_POLARITY_6 (1 << 5)
#define DI_GEN_POLARITY_7 (1 << 6)
#define DI_GEN_POLARITY_8 (1 << 7)
#define DI_GEN_POLARITY_DISP_CLK (1 << 17)
#define DI_GEN_DI_CLK_EXT (1 << 20)
#define DI_GEN_DI_VSYNC_EXT (1 << 21)
#define DI_POL_DRDY_DATA_POLARITY (1 << 7)
#define DI_POL_DRDY_POLARITY_15 (1 << 4)
#define DI_VSYNC_SEL_OFFSET 13
static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)
{
return readl(di->base + offset);
}
static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
{
ipuwritel("di", value, di->base + offset);
}
static int ipu_di_clk_calc_div(unsigned long inrate, unsigned long outrate)
{
u64 tmp = inrate;
int div;
tmp *= 16;
do_div(tmp, outrate);
div = tmp;
if (div < 0x10)
div = 0x10;
#ifdef WTF_IS_THIS
/*
* Freescale has this in their Kernel. It is neither clear what
* it does nor why it does it
*/
if (div & 0x10)
div &= ~0x7;
else {
/* Round up divider if it gets us closer to desired pix clk */
if ((div & 0xC) == 0xC) {
div += 0x10;
div &= ~0xF;
}
}
#endif
return div;
}
static unsigned long clk_di_recalc_rate(struct clk *clk,
unsigned long parent_rate)
{
struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
unsigned long outrate;
u32 div = ipu_di_read(di, DI_BS_CLKGEN0);
if (div < 0x10)
div = 0x10;
outrate = (parent_rate / div) * 16;
return outrate;
}
static long clk_di_round_rate(struct clk *clk, unsigned long rate,
unsigned long *prate)
{
struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
unsigned long outrate;
int div;
u32 val;
div = ipu_di_clk_calc_div(*prate, rate);
outrate = (*prate / div) * 16;
val = ipu_di_read(di, DI_GENERAL);
if (!(val & DI_GEN_DI_CLK_EXT) && outrate > *prate / 2)
outrate = *prate / 2;
dev_dbg(di->ipu->dev,
"%s: inrate: %ld div: 0x%08x outrate: %ld wanted: %ld\n",
__func__, *prate, div, outrate, rate);
return outrate;
}
static int clk_di_set_rate(struct clk *clk, unsigned long rate,
unsigned long parent_rate)
{
struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
int div;
u32 clkgen0;
clkgen0 = ipu_di_read(di, DI_BS_CLKGEN0) & ~0xfff;
div = ipu_di_clk_calc_div(parent_rate, rate);
ipu_di_write(di, clkgen0 | div, DI_BS_CLKGEN0);
dev_dbg(di->ipu->dev, "%s: inrate: %ld desired: %ld div: 0x%08x\n",
__func__, parent_rate, rate, div);
return 0;
}
static int clk_di_get_parent(struct clk *clk)
{
struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
u32 val;
val = ipu_di_read(di, DI_GENERAL);
return val & DI_GEN_DI_CLK_EXT ? 1 : 0;
}
static int clk_di_set_parent(struct clk *clk, u8 index)
{
struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
u32 val;
val = ipu_di_read(di, DI_GENERAL);
if (index)
val |= DI_GEN_DI_CLK_EXT;
else
val &= ~DI_GEN_DI_CLK_EXT;
ipu_di_write(di, val, DI_GENERAL);
return 0;
}
static struct clk_ops clk_di_ops = {
.round_rate = clk_di_round_rate,
.set_rate = clk_di_set_rate,
.recalc_rate = clk_di_recalc_rate,
.set_parent = clk_di_set_parent,
.get_parent = clk_di_get_parent,
};
static void ipu_di_data_wave_config(struct ipu_di *di,
int wave_gen,
int access_size, int component_size)
{
u32 reg;
reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) |
(component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET);
ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
}
static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin,
int set, int up, int down)
{
u32 reg;
reg = ipu_di_read(di, DI_DW_GEN(wave_gen));
reg &= ~(0x3 << (di_pin * 2));
reg |= set << (di_pin * 2);
ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set));
}
static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config,
int start, int count)
{
u32 reg;
int i;
for (i = 0; i < count; i++) {
struct di_sync_config *c = &config[i];
int wave_gen = start + i + 1;
if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) ||
(c->repeat_count >= 0x1000) ||
(c->cnt_up >= 0x400) ||
(c->cnt_down >= 0x400)) {
dev_err(di->ipu->dev, "DI%d counters out of range.\n",
di->id);
return;
}
reg = DI_SW_GEN0_RUN_COUNT(c->run_count) |
DI_SW_GEN0_RUN_SRC(c->run_src) |
DI_SW_GEN0_OFFSET_COUNT(c->offset_count) |
DI_SW_GEN0_OFFSET_SRC(c->offset_src);
ipu_di_write(di, reg, DI_SW_GEN0(wave_gen));
reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) |
DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) |
DI_SW_GEN1_CNT_POL_TRIGGER_SRC(
c->cnt_polarity_trigger_src) |
DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) |
DI_SW_GEN1_CNT_DOWN(c->cnt_down) |
DI_SW_GEN1_CNT_UP(c->cnt_up);
/* Enable auto reload */
if (c->repeat_count == 0)
reg |= DI_SW_GEN1_AUTO_RELOAD;
ipu_di_write(di, reg, DI_SW_GEN1(wave_gen));
reg = ipu_di_read(di, DI_STP_REP(wave_gen));
reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1)));
reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1));
ipu_di_write(di, reg, DI_STP_REP(wave_gen));
}
}
static void ipu_di_sync_config_interlaced(struct ipu_di *di,
struct ipu_di_signal_cfg *sig)
{
u32 h_total = sig->width + sig->h_sync_width +
sig->h_start_width + sig->h_end_width;
u32 v_total = sig->height + sig->v_sync_width +
sig->v_start_width + sig->v_end_width;
u32 reg;
struct di_sync_config cfg[] = {
{
.run_count = h_total / 2 - 1,
.run_src = DI_SYNC_CLK,
}, {
.run_count = h_total - 11,
.run_src = DI_SYNC_CLK,
.cnt_down = 4,
}, {
.run_count = v_total * 2 - 1,
.run_src = DI_SYNC_INT_HSYNC,
.offset_count = 1,
.offset_src = DI_SYNC_INT_HSYNC,
.cnt_down = 4,
}, {
.run_count = v_total / 2 - 1,
.run_src = DI_SYNC_HSYNC,
.offset_count = sig->v_start_width,
.offset_src = DI_SYNC_HSYNC,
.repeat_count = 2,
.cnt_clr_src = DI_SYNC_VSYNC,
}, {
.run_src = DI_SYNC_HSYNC,
.repeat_count = sig->height / 2,
.cnt_clr_src = 4,
}, {
.run_count = v_total - 1,
.run_src = DI_SYNC_HSYNC,
}, {
.run_count = v_total / 2 - 1,
.run_src = DI_SYNC_HSYNC,
.offset_count = 9,
.offset_src = DI_SYNC_HSYNC,
.repeat_count = 2,
.cnt_clr_src = DI_SYNC_VSYNC,
}, {
.run_src = DI_SYNC_CLK,
.offset_count = sig->h_start_width,
.offset_src = DI_SYNC_CLK,
.repeat_count = sig->width,
.cnt_clr_src = 5,
}, {
.run_count = v_total - 1,
.run_src = DI_SYNC_INT_HSYNC,
.offset_count = v_total / 2,
.offset_src = DI_SYNC_INT_HSYNC,
.cnt_clr_src = DI_SYNC_HSYNC,
.cnt_down = 4,
}
};
ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
/* set gentime select and tag sel */
reg = ipu_di_read(di, DI_SW_GEN1(9));
reg &= 0x1FFFFFFF;
reg |= (3 - 1) << 29 | 0x00008000;
ipu_di_write(di, reg, DI_SW_GEN1(9));
ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
}
static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
struct ipu_di_signal_cfg *sig, int div)
{
u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width +
sig->h_end_width;
u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width +
sig->v_end_width;
struct di_sync_config cfg[] = {
{
/* 1: INT_HSYNC */
.run_count = h_total - 1,
.run_src = DI_SYNC_CLK,
} , {
/* PIN2: HSYNC */
.run_count = h_total - 1,
.run_src = DI_SYNC_CLK,
.offset_count = div * sig->v_to_h_sync,
.offset_src = DI_SYNC_CLK,
.cnt_polarity_gen_en = 1,
.cnt_polarity_trigger_src = DI_SYNC_CLK,
.cnt_down = sig->h_sync_width * 2,
} , {
/* PIN3: VSYNC */
.run_count = v_total - 1,
.run_src = DI_SYNC_INT_HSYNC,
.cnt_polarity_gen_en = 1,
.cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
.cnt_down = sig->v_sync_width * 2,
} , {
/* 4: Line Active */
.run_src = DI_SYNC_HSYNC,
.offset_count = sig->v_sync_width + sig->v_start_width,
.offset_src = DI_SYNC_HSYNC,
.repeat_count = sig->height,
.cnt_clr_src = DI_SYNC_VSYNC,
} , {
/* 5: Pixel Active, referenced by DC */
.run_src = DI_SYNC_CLK,
.offset_count = sig->h_sync_width + sig->h_start_width,
.offset_src = DI_SYNC_CLK,
.repeat_count = sig->width,
.cnt_clr_src = 5, /* Line Active */
} , {
/* unused */
} , {
/* unused */
} , {
/* unused */
} , {
/* unused */
},
};
/* can't use #7 and #8 for line active and pixel active counters */
struct di_sync_config cfg_vga[] = {
{
/* 1: INT_HSYNC */
.run_count = h_total - 1,
.run_src = DI_SYNC_CLK,
} , {
/* 2: VSYNC */
.run_count = v_total - 1,
.run_src = DI_SYNC_INT_HSYNC,
} , {
/* 3: Line Active */
.run_src = DI_SYNC_INT_HSYNC,
.offset_count = sig->v_sync_width + sig->v_start_width,
.offset_src = DI_SYNC_INT_HSYNC,
.repeat_count = sig->height,
.cnt_clr_src = 3 /* VSYNC */,
} , {
/* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */
.run_count = h_total - 1,
.run_src = DI_SYNC_CLK,
.offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */
.offset_src = DI_SYNC_CLK,
.cnt_polarity_gen_en = 1,
.cnt_polarity_trigger_src = DI_SYNC_CLK,
.cnt_down = sig->h_sync_width * 2,
} , {
/* 5: Pixel Active signal to DC */
.run_src = DI_SYNC_CLK,
.offset_count = sig->h_sync_width + sig->h_start_width,
.offset_src = DI_SYNC_CLK,
.repeat_count = sig->width,
.cnt_clr_src = 4, /* Line Active */
} , {
/* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */
.run_count = v_total - 1,
.run_src = DI_SYNC_INT_HSYNC,
.offset_count = 1, /* magic value from Freescale TVE driver */
.offset_src = DI_SYNC_INT_HSYNC,
.cnt_polarity_gen_en = 1,
.cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
.cnt_down = sig->v_sync_width * 2,
} , {
/* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */
.run_count = h_total - 1,
.run_src = DI_SYNC_CLK,
.offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */
.offset_src = DI_SYNC_CLK,
.cnt_polarity_gen_en = 1,
.cnt_polarity_trigger_src = DI_SYNC_CLK,
.cnt_down = sig->h_sync_width * 2,
} , {
/* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */
.run_count = v_total - 1,
.run_src = DI_SYNC_INT_HSYNC,
.offset_count = 1, /* magic value from Freescale TVE driver */
.offset_src = DI_SYNC_INT_HSYNC,
.cnt_polarity_gen_en = 1,
.cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
.cnt_down = sig->v_sync_width * 2,
} , {
/* unused */
},
};
ipu_di_write(di, v_total - 1, DI_SCR_CONF);
if (sig->hsync_pin == 2 && sig->vsync_pin == 3)
ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
else
ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga));
}
int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
{
u32 reg;
u32 di_gen, vsync_cnt;
u32 div;
u32 h_total, v_total;
int ret;
unsigned long round;
struct clk *parent;
dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d, pixelclock = %ld\n",
di->id, sig->width, sig->height, sig->pixelclock);
if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
return -EINVAL;
if (sig->clkflags & IPU_DI_CLKMODE_EXT)
parent = di->clk_di;
else
parent = di->clk_ipu;
ret = clk_set_parent(&di->clk_di_pixel, parent);
if (ret) {
dev_err(di->ipu->dev,
"setting pixel clock to parent %s failed with %d\n",
parent->name, ret);
return ret;
}
if (sig->clkflags & IPU_DI_CLKMODE_SYNC) {
ret = clk_set_rate(clk_get_parent(parent), sig->pixelclock);
round = sig->pixelclock;
} else if (sig->clkflags & IPU_DI_CLKMODE_NON_FRACTIONAL) {
unsigned div;
round = clk_get_rate(parent);
div = DIV_ROUND_CLOSEST(round, sig->pixelclock);
round = round / div;
} else {
round = sig->pixelclock;
}
ret = clk_set_rate(&di->clk_di_pixel, round);
h_total = sig->width + sig->h_sync_width + sig->h_start_width +
sig->h_end_width;
v_total = sig->height + sig->v_sync_width + sig->v_start_width +
sig->v_end_width;
div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;
div = div / 16; /* Now divider is integer portion */
/* Setup pixel clock timing */
/* Down time is half of period */
ipu_di_write(di, (div << 16), DI_BS_CLKGEN1);
ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1);
ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);
di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT;
di_gen |= DI_GEN_DI_VSYNC_EXT;
if (sig->interlaced) {
ipu_di_sync_config_interlaced(di, sig);
/* set y_sel = 1 */
di_gen |= 0x10000000;
di_gen |= DI_GEN_POLARITY_5;
di_gen |= DI_GEN_POLARITY_8;
vsync_cnt = 7;
if (sig->Hsync_pol)
di_gen |= DI_GEN_POLARITY_3;
if (sig->Vsync_pol)
di_gen |= DI_GEN_POLARITY_2;
} else {
ipu_di_sync_config_noninterlaced(di, sig, div);
vsync_cnt = 3;
if (di->id == 1)
/*
* TODO: change only for TVEv2, parallel display
* uses pin 2 / 3
*/
if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3))
vsync_cnt = 6;
if (sig->Hsync_pol) {
if (sig->hsync_pin == 2)
di_gen |= DI_GEN_POLARITY_2;
else if (sig->hsync_pin == 4)
di_gen |= DI_GEN_POLARITY_4;
else if (sig->hsync_pin == 7)
di_gen |= DI_GEN_POLARITY_7;
}
if (sig->Vsync_pol) {
if (sig->vsync_pin == 3)
di_gen |= DI_GEN_POLARITY_3;
else if (sig->vsync_pin == 6)
di_gen |= DI_GEN_POLARITY_6;
else if (sig->vsync_pin == 8)
di_gen |= DI_GEN_POLARITY_8;
}
}
if (!sig->clk_pol)
di_gen |= DI_GEN_POLARITY_DISP_CLK;
ipu_di_write(di, di_gen, DI_GENERAL);
ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
DI_SYNC_AS_GEN);
reg = ipu_di_read(di, DI_POL);
reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);
if (sig->enable_pol)
reg |= DI_POL_DRDY_POLARITY_15;
if (sig->data_pol)
reg |= DI_POL_DRDY_DATA_POLARITY;
ipu_di_write(di, reg, DI_POL);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);
int ipu_di_enable(struct ipu_di *di)
{
int ret = clk_enable(&di->clk_di_pixel);
if (ret)
return ret;
ipu_module_enable(di->ipu, di->module);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_di_enable);
int ipu_di_disable(struct ipu_di *di)
{
ipu_module_disable(di->ipu, di->module);
clk_disable(&di->clk_di_pixel);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_di_disable);
int ipu_di_get_num(struct ipu_di *di)
{
return di->id;
}
EXPORT_SYMBOL_GPL(ipu_di_get_num);
struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp)
{
struct ipu_di *di;
if (disp > 1)
return ERR_PTR(-EINVAL);
di = ipu->di_priv[disp];
if (di->inuse) {
di = ERR_PTR(-EBUSY);
goto out;
}
di->inuse = true;
out:
return di;
}
EXPORT_SYMBOL_GPL(ipu_di_get);
void ipu_di_put(struct ipu_di *di)
{
di->inuse = false;
}
EXPORT_SYMBOL_GPL(ipu_di_put);
int ipu_di_init(struct ipu_soc *ipu, struct device_d *dev, int id,
void __iomem *base,
u32 module, struct clk *clk_ipu)
{
struct ipu_di *di;
int ret;
if (id > 1)
return -ENODEV;
di = xzalloc(sizeof(*di));
ipu->di_priv[id] = di;
di->clk_di = clk_get(dev, id ? "di1" : "di0");
if (IS_ERR(di->clk_di))
return PTR_ERR(di->clk_di);
di->module = module;
di->id = id;
di->clk_ipu = clk_ipu;
di->base = base;
di->di_parent_names[0] = di->clk_ipu->name;
di->di_parent_names[1] = di->clk_di->name;
ipu_di_write(di, 0x10, DI_BS_CLKGEN0);
di->clk_di_pixel.parent_names = di->di_parent_names;
di->clk_name = asprintf("%s_di%d_pixel",
dev_name(dev), id);
if (!di->clk_name)
return -ENOMEM;
di->clk_di_pixel.ops = &clk_di_ops;
di->clk_di_pixel.num_parents = 2;
di->clk_di_pixel.name = di->clk_name;
ret = clk_register(&di->clk_di_pixel);
if (ret)
goto failed_clk_register;
dev_dbg(dev, "DI%d base: 0x%p\n", id, base);
di->inuse = false;
di->ipu = ipu;
return 0;
failed_clk_register:
free(di->clk_name);
return ret;
}
void ipu_di_exit(struct ipu_soc *ipu, int id)
{
}

View File

@ -0,0 +1,397 @@
/*
* Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
* Copyright (C) 2005-2009 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.
*/
#include <common.h>
#include <linux/err.h>
#include <linux/clk.h>
#include "imx-ipu-v3.h"
#include "ipu-prv.h"
#define DMFC_RD_CHAN 0x0000
#define DMFC_WR_CHAN 0x0004
#define DMFC_WR_CHAN_DEF 0x0008
#define DMFC_DP_CHAN 0x000c
#define DMFC_DP_CHAN_DEF 0x0010
#define DMFC_GENERAL1 0x0014
#define DMFC_GENERAL2 0x0018
#define DMFC_IC_CTRL 0x001c
#define DMFC_STAT 0x0020
#define DMFC_WR_CHAN_1_28 0
#define DMFC_WR_CHAN_2_41 8
#define DMFC_WR_CHAN_1C_42 16
#define DMFC_WR_CHAN_2C_43 24
#define DMFC_DP_CHAN_5B_23 0
#define DMFC_DP_CHAN_5F_27 8
#define DMFC_DP_CHAN_6B_24 16
#define DMFC_DP_CHAN_6F_29 24
#define DMFC_FIFO_SIZE_64 (3 << 3)
#define DMFC_FIFO_SIZE_128 (2 << 3)
#define DMFC_FIFO_SIZE_256 (1 << 3)
#define DMFC_FIFO_SIZE_512 (0 << 3)
#define DMFC_SEGMENT(x) ((x & 0x7) << 0)
#define DMFC_BURSTSIZE_128 (0 << 6)
#define DMFC_BURSTSIZE_64 (1 << 6)
#define DMFC_BURSTSIZE_32 (2 << 6)
#define DMFC_BURSTSIZE_16 (3 << 6)
struct dmfc_channel_data {
int ipu_channel;
unsigned long channel_reg;
unsigned long shift;
unsigned eot_shift;
unsigned max_fifo_lines;
};
static const struct dmfc_channel_data dmfcdata[] = {
{
.ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC,
.channel_reg = DMFC_DP_CHAN,
.shift = DMFC_DP_CHAN_5B_23,
.eot_shift = 20,
.max_fifo_lines = 3,
}, {
.ipu_channel = 24,
.channel_reg = DMFC_DP_CHAN,
.shift = DMFC_DP_CHAN_6B_24,
.eot_shift = 22,
.max_fifo_lines = 1,
}, {
.ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC,
.channel_reg = DMFC_DP_CHAN,
.shift = DMFC_DP_CHAN_5F_27,
.eot_shift = 21,
.max_fifo_lines = 2,
}, {
.ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC,
.channel_reg = DMFC_WR_CHAN,
.shift = DMFC_WR_CHAN_1_28,
.eot_shift = 16,
.max_fifo_lines = 2,
}, {
.ipu_channel = 29,
.channel_reg = DMFC_DP_CHAN,
.shift = DMFC_DP_CHAN_6F_29,
.eot_shift = 23,
.max_fifo_lines = 1,
},
};
#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata)
struct ipu_dmfc_priv;
struct dmfc_channel {
unsigned slots;
unsigned slotmask;
unsigned segment;
int burstsize;
struct ipu_soc *ipu;
struct ipu_dmfc_priv *priv;
const struct dmfc_channel_data *data;
};
struct ipu_dmfc_priv {
struct ipu_soc *ipu;
struct device_d *dev;
struct dmfc_channel channels[DMFC_NUM_CHANNELS];
unsigned long bandwidth_per_slot;
void __iomem *base;
int use_count;
};
int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
{
struct ipu_dmfc_priv *priv = dmfc->priv;
if (!priv->use_count)
ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN);
priv->use_count++;
return 0;
}
EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
{
struct ipu_dmfc_priv *priv = dmfc->priv;
priv->use_count--;
if (!priv->use_count)
ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
if (priv->use_count < 0)
priv->use_count = 0;
}
EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel);
static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots,
int segment, int burstsize)
{
struct ipu_dmfc_priv *priv = dmfc->priv;
u32 val, field;
dev_dbg(priv->dev,
"dmfc: using %d slots starting from segment %d for IPU channel %d\n",
slots, segment, dmfc->data->ipu_channel);
if (!dmfc)
return -EINVAL;
switch (slots) {
case 1:
field = DMFC_FIFO_SIZE_64;
break;
case 2:
field = DMFC_FIFO_SIZE_128;
break;
case 4:
field = DMFC_FIFO_SIZE_256;
break;
case 8:
field = DMFC_FIFO_SIZE_512;
break;
default:
return -EINVAL;
}
switch (burstsize) {
case 16:
field |= DMFC_BURSTSIZE_16;
break;
case 32:
field |= DMFC_BURSTSIZE_32;
break;
case 64:
field |= DMFC_BURSTSIZE_64;
break;
case 128:
field |= DMFC_BURSTSIZE_128;
break;
}
field |= DMFC_SEGMENT(segment);
val = ipureadl(priv->base + dmfc->data->channel_reg);
val &= ~(0xff << dmfc->data->shift);
val |= field << dmfc->data->shift;
ipuwritel("dmfc", val, priv->base + dmfc->data->channel_reg);
dmfc->slots = slots;
dmfc->segment = segment;
dmfc->burstsize = burstsize;
dmfc->slotmask = ((1 << slots) - 1) << segment;
return 0;
}
static int dmfc_bandwidth_to_slots(struct ipu_dmfc_priv *priv,
unsigned long bandwidth)
{
int slots = 1;
while (slots * priv->bandwidth_per_slot < bandwidth)
slots *= 2;
return slots;
}
static int dmfc_find_slots(struct ipu_dmfc_priv *priv, int slots)
{
unsigned slotmask_need, slotmask_used = 0;
int i, segment = 0;
slotmask_need = (1 << slots) - 1;
for (i = 0; i < DMFC_NUM_CHANNELS; i++)
slotmask_used |= priv->channels[i].slotmask;
while (slotmask_need <= 0xff) {
if (!(slotmask_used & slotmask_need))
return segment;
slotmask_need <<= 1;
segment++;
}
return -EBUSY;
}
void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc)
{
struct ipu_dmfc_priv *priv = dmfc->priv;
int i;
dev_dbg(priv->dev, "dmfc: freeing %d slots starting from segment %d\n",
dmfc->slots, dmfc->segment);
if (!dmfc->slots)
return;
dmfc->slotmask = 0;
dmfc->slots = 0;
dmfc->segment = 0;
for (i = 0; i < DMFC_NUM_CHANNELS; i++)
priv->channels[i].slotmask = 0;
for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
if (priv->channels[i].slots > 0) {
priv->channels[i].segment =
dmfc_find_slots(priv, priv->channels[i].slots);
priv->channels[i].slotmask =
((1 << priv->channels[i].slots) - 1) <<
priv->channels[i].segment;
}
}
for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
if (priv->channels[i].slots > 0)
ipu_dmfc_setup_channel(&priv->channels[i],
priv->channels[i].slots,
priv->channels[i].segment,
priv->channels[i].burstsize);
}
}
EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth);
int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
unsigned long bandwidth_pixel_per_second, int burstsize)
{
struct ipu_dmfc_priv *priv = dmfc->priv;
int slots = dmfc_bandwidth_to_slots(priv, bandwidth_pixel_per_second);
int segment = -1, ret = 0;
dev_dbg(priv->dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
bandwidth_pixel_per_second / 1000000,
dmfc->data->ipu_channel);
ipu_dmfc_free_bandwidth(dmfc);
if (slots > 8) {
ret = -EBUSY;
goto out;
}
/* For the MEM_BG channel, first try to allocate twice the slots */
if (dmfc->data->ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC)
segment = dmfc_find_slots(priv, slots * 2);
else if (slots < 2)
/* Always allocate at least 128*4 bytes (2 slots) */
slots = 2;
if (segment >= 0)
slots *= 2;
else
segment = dmfc_find_slots(priv, slots);
if (segment < 0) {
ret = -EBUSY;
goto out;
}
ipu_dmfc_setup_channel(dmfc, slots, segment, burstsize);
out:
return ret;
}
EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth);
int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
{
struct ipu_dmfc_priv *priv = dmfc->priv;
u32 dmfc_gen1;
dmfc_gen1 = ipureadl(priv->base + DMFC_GENERAL1);
if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines)
dmfc_gen1 |= 1 << dmfc->data->eot_shift;
else
dmfc_gen1 &= ~(1 << dmfc->data->eot_shift);
ipuwritel("dmfc", dmfc_gen1, priv->base + DMFC_GENERAL1);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_dmfc_init_channel);
struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel)
{
struct ipu_dmfc_priv *priv = ipu->dmfc_priv;
int i;
for (i = 0; i < DMFC_NUM_CHANNELS; i++)
if (dmfcdata[i].ipu_channel == ipu_channel)
return &priv->channels[i];
return ERR_PTR(-ENODEV);
}
EXPORT_SYMBOL_GPL(ipu_dmfc_get);
void ipu_dmfc_put(struct dmfc_channel *dmfc)
{
ipu_dmfc_free_bandwidth(dmfc);
}
EXPORT_SYMBOL_GPL(ipu_dmfc_put);
int ipu_dmfc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base,
struct clk *ipu_clk)
{
struct ipu_dmfc_priv *priv;
int i;
priv = xzalloc(sizeof(*priv));
priv->base = base;
priv->dev = dev;
priv->ipu = ipu;
ipu->dmfc_priv = priv;
for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
priv->channels[i].priv = priv;
priv->channels[i].ipu = ipu;
priv->channels[i].data = &dmfcdata[i];
}
ipuwritel("dmfc", 0x0, priv->base + DMFC_WR_CHAN);
ipuwritel("dmfc", 0x0, priv->base + DMFC_DP_CHAN);
/*
* We have a total bandwidth of clkrate * 4pixel divided
* into 8 slots.
*/
priv->bandwidth_per_slot = clk_get_rate(ipu_clk) * 4 / 8;
dev_dbg(dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
priv->bandwidth_per_slot / 1000000);
ipuwritel("dmfc", 0x202020f6, priv->base + DMFC_WR_CHAN_DEF);
ipuwritel("dmfc", 0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF);
ipuwritel("dmfc", 0x00000003, priv->base + DMFC_GENERAL1);
return 0;
}
void ipu_dmfc_exit(struct ipu_soc *ipu)
{
}

View File

@ -0,0 +1,313 @@
/*
* Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
* Copyright (C) 2005-2009 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.
*/
#include <common.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <malloc.h>
#include "imx-ipu-v3.h"
#include "ipu-prv.h"
#define DP_SYNC 0
#define DP_ASYNC0 0x60
#define DP_ASYNC1 0xBC
#define DP_COM_CONF 0x0
#define DP_GRAPH_WIND_CTRL 0x0004
#define DP_FG_POS 0x0008
#define DP_CSC_A_0 0x0044
#define DP_CSC_A_1 0x0048
#define DP_CSC_A_2 0x004C
#define DP_CSC_A_3 0x0050
#define DP_CSC_0 0x0054
#define DP_CSC_1 0x0058
#define DP_COM_CONF_FG_EN (1 << 0)
#define DP_COM_CONF_GWSEL (1 << 1)
#define DP_COM_CONF_GWAM (1 << 2)
#define DP_COM_CONF_GWCKE (1 << 3)
#define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
#define DP_COM_CONF_CSC_DEF_OFFSET 8
#define DP_COM_CONF_CSC_DEF_FG (3 << 8)
#define DP_COM_CONF_CSC_DEF_BG (2 << 8)
#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
#define IPUV3_NUM_FLOWS 3
struct ipu_dp_priv;
struct ipu_dp {
u32 flow;
bool in_use;
bool foreground;
enum ipu_color_space in_cs;
};
struct ipu_flow {
struct ipu_dp foreground;
struct ipu_dp background;
enum ipu_color_space out_cs;
void __iomem *base;
struct ipu_dp_priv *priv;
};
struct ipu_dp_priv {
struct ipu_soc *ipu;
struct device_d *dev;
void __iomem *base;
struct ipu_flow flow[IPUV3_NUM_FLOWS];
int use_count;
};
static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
{
if (dp->foreground)
return container_of(dp, struct ipu_flow, foreground);
else
return container_of(dp, struct ipu_flow, background);
}
int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
u8 alpha, bool bg_chan)
{
struct ipu_flow *flow = to_flow(dp);
struct ipu_dp_priv *priv = flow->priv;
u32 reg;
reg = ipureadl(flow->base + DP_COM_CONF);
if (bg_chan)
reg &= ~DP_COM_CONF_GWSEL;
else
reg |= DP_COM_CONF_GWSEL;
ipuwritel("dp", reg, flow->base + DP_COM_CONF);
if (enable) {
reg = ipureadl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
ipuwritel("dp", reg | ((u32) alpha << 24),
flow->base + DP_GRAPH_WIND_CTRL);
reg = ipureadl(flow->base + DP_COM_CONF);
ipuwritel("dp", reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
} else {
reg = ipureadl(flow->base + DP_COM_CONF);
ipuwritel("dp", reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
}
ipu_srm_dp_sync_update(priv->ipu);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
{
struct ipu_flow *flow = to_flow(dp);
struct ipu_dp_priv *priv = flow->priv;
ipuwritel("dp", (x_pos << 16) | y_pos, flow->base + DP_FG_POS);
ipu_srm_dp_sync_update(priv->ipu);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
static void ipu_dp_csc_init(struct ipu_flow *flow,
enum ipu_color_space in,
enum ipu_color_space out,
u32 place)
{
u32 reg;
reg = ipureadl(flow->base + DP_COM_CONF);
reg &= ~DP_COM_CONF_CSC_DEF_MASK;
if (in == out) {
ipuwritel("dp", reg, flow->base + DP_COM_CONF);
return;
}
if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
ipuwritel("dp", 0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
ipuwritel("dp", 0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
ipuwritel("dp", 0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
ipuwritel("dp", 0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
ipuwritel("dp", 0x3d6 | (0x0000 << 16) | (2 << 30),
flow->base + DP_CSC_0);
ipuwritel("dp", 0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
flow->base + DP_CSC_1);
} else {
ipuwritel("dp", 0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
ipuwritel("dp", 0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
ipuwritel("dp", 0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
ipuwritel("dp", 0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
ipuwritel("dp", 0x000 | (0x3e42 << 16) | (1 << 30),
flow->base + DP_CSC_0);
ipuwritel("dp", 0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
flow->base + DP_CSC_1);
}
reg |= place;
ipuwritel("dp", reg, flow->base + DP_COM_CONF);
}
int ipu_dp_setup_channel(struct ipu_dp *dp,
enum ipu_color_space in,
enum ipu_color_space out)
{
struct ipu_flow *flow = to_flow(dp);
struct ipu_dp_priv *priv = flow->priv;
dp->in_cs = in;
if (!dp->foreground)
flow->out_cs = out;
if (flow->foreground.in_cs == flow->background.in_cs) {
/*
* foreground and background are of same colorspace, put
* colorspace converter after combining unit.
*/
ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs,
DP_COM_CONF_CSC_DEF_BOTH);
} else {
if (flow->foreground.in_cs == flow->out_cs)
/*
* foreground identical to output, apply color
* conversion on background
*/
ipu_dp_csc_init(flow, flow->background.in_cs,
flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
else
ipu_dp_csc_init(flow, flow->foreground.in_cs,
flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
}
ipu_srm_dp_sync_update(priv->ipu);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
int ipu_dp_enable_channel(struct ipu_dp *dp)
{
struct ipu_flow *flow = to_flow(dp);
struct ipu_dp_priv *priv = flow->priv;
if (!priv->use_count)
ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
priv->use_count++;
if (dp->foreground) {
u32 reg;
reg = ipureadl(flow->base + DP_COM_CONF);
reg |= DP_COM_CONF_FG_EN;
ipuwritel("dp", reg, flow->base + DP_COM_CONF);
ipu_srm_dp_sync_update(priv->ipu);
}
return 0;
}
EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
void ipu_dp_disable_channel(struct ipu_dp *dp)
{
struct ipu_flow *flow = to_flow(dp);
struct ipu_dp_priv *priv = flow->priv;
priv->use_count--;
if (dp->foreground) {
u32 reg, csc;
reg = ipureadl(flow->base + DP_COM_CONF);
csc = reg & DP_COM_CONF_CSC_DEF_MASK;
if (csc == DP_COM_CONF_CSC_DEF_FG)
reg &= ~DP_COM_CONF_CSC_DEF_MASK;
reg &= ~DP_COM_CONF_FG_EN;
ipuwritel("dp", reg, flow->base + DP_COM_CONF);
ipuwritel("dp", 0, flow->base + DP_FG_POS);
ipu_srm_dp_sync_update(priv->ipu);
}
if (!priv->use_count)
ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
if (priv->use_count < 0)
priv->use_count = 0;
}
EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
{
struct ipu_dp_priv *priv = ipu->dp_priv;
struct ipu_dp *dp;
if ((flow >> 1) >= IPUV3_NUM_FLOWS)
return ERR_PTR(-EINVAL);
if (flow & 1)
dp = &priv->flow[flow >> 1].foreground;
else
dp = &priv->flow[flow >> 1].background;
if (dp->in_use)
return ERR_PTR(-EBUSY);
dp->in_use = true;
return dp;
}
EXPORT_SYMBOL_GPL(ipu_dp_get);
void ipu_dp_put(struct ipu_dp *dp)
{
dp->in_use = false;
}
EXPORT_SYMBOL_GPL(ipu_dp_put);
int ipu_dp_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base)
{
struct ipu_dp_priv *priv;
int i;
priv = xzalloc(sizeof(*priv));
priv->dev = dev;
priv->ipu = ipu;
ipu->dp_priv = priv;
priv->base = base;
for (i = 0; i < IPUV3_NUM_FLOWS; i++) {
priv->flow[i].foreground.foreground = true;
priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
priv->flow[i].priv = priv;
}
return 0;
}
void ipu_dp_exit(struct ipu_soc *ipu)
{
}

View File

@ -0,0 +1,204 @@
/*
* Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
* Copyright (C) 2005-2009 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 __IPU_PRV_H__
#define __IPU_PRV_H__
struct ipu_soc;
#include "imx-ipu-v3.h"
#define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3')
#define IPUV3_CHANNEL_CSI0 0
#define IPUV3_CHANNEL_CSI1 1
#define IPUV3_CHANNEL_CSI2 2
#define IPUV3_CHANNEL_CSI3 3
#define IPUV3_CHANNEL_MEM_BG_SYNC 23
#define IPUV3_CHANNEL_MEM_FG_SYNC 27
#define IPUV3_CHANNEL_MEM_DC_SYNC 28
#define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA 31
#define IPUV3_CHANNEL_MEM_DC_ASYNC 41
#define IPUV3_CHANNEL_ROT_ENC_MEM 45
#define IPUV3_CHANNEL_ROT_VF_MEM 46
#define IPUV3_CHANNEL_ROT_PP_MEM 47
#define IPUV3_CHANNEL_ROT_ENC_MEM_OUT 48
#define IPUV3_CHANNEL_ROT_VF_MEM_OUT 49
#define IPUV3_CHANNEL_ROT_PP_MEM_OUT 50
#define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA 51
#define IPU_MCU_T_DEFAULT 8
#define IPU_CM_IDMAC_REG_OFS 0x00008000
#define IPU_CM_IC_REG_OFS 0x00020000
#define IPU_CM_IRT_REG_OFS 0x00028000
#define IPU_CM_CSI0_REG_OFS 0x00030000
#define IPU_CM_CSI1_REG_OFS 0x00038000
#define IPU_CM_SMFC_REG_OFS 0x00050000
#define IPU_CM_DC_REG_OFS 0x00058000
#define IPU_CM_DMFC_REG_OFS 0x00060000
/* 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))
#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32))
#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244)
#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248)
#define IPU_SRM_STAT IPU_CM_REG(0x024C)
#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250)
#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254)
#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32))
#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32))
#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32))
#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32))
#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n))
#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n))
#define IPU_DI0_COUNTER_RELEASE (1 << 24)
#define IPU_DI1_COUNTER_RELEASE (1 << 25)
#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 IPU_IDMAC_REG(0x0024)
#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028)
#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c)
#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030)
#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034)
#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32))
#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32))
#define IPU_NUM_IRQS (32 * 15)
enum ipu_modules {
IPU_CONF_CSI0_EN = (1 << 0),
IPU_CONF_CSI1_EN = (1 << 1),
IPU_CONF_IC_EN = (1 << 2),
IPU_CONF_ROT_EN = (1 << 3),
IPU_CONF_ISP_EN = (1 << 4),
IPU_CONF_DP_EN = (1 << 5),
IPU_CONF_DI0_EN = (1 << 6),
IPU_CONF_DI1_EN = (1 << 7),
IPU_CONF_SMFC_EN = (1 << 8),
IPU_CONF_DC_EN = (1 << 9),
IPU_CONF_DMFC_EN = (1 << 10),
IPU_CONF_VDI_EN = (1 << 12),
IPU_CONF_IDMAC_DIS = (1 << 22),
IPU_CONF_IC_DMFC_SEL = (1 << 25),
IPU_CONF_IC_DMFC_SYNC = (1 << 26),
IPU_CONF_VDI_DMFC_SYNC = (1 << 27),
IPU_CONF_CSI0_DATA_SOURCE = (1 << 28),
IPU_CONF_CSI1_DATA_SOURCE = (1 << 29),
IPU_CONF_IC_INPUT = (1 << 30),
IPU_CONF_CSI_SEL = (1 << 31),
};
struct ipuv3_channel {
unsigned int num;
bool enabled;
bool busy;
struct ipu_soc *ipu;
};
struct ipu_dc_priv;
struct ipu_dmfc_priv;
struct ipu_di;
struct ipu_devtype;
struct ipu_soc {
struct device_d *dev;
const struct ipu_devtype *devtype;
enum ipuv3_type ipu_type;
spinlock_t lock;
struct mutex channel_lock;
void __iomem *base;
void __iomem *cm_reg;
void __iomem *idmac_reg;
struct ipu_ch_param __iomem *cpmem_base;
int usecount;
struct clk *clk;
struct ipuv3_channel channel[64];
int irq_sync;
int irq_err;
struct irq_domain *domain;
struct ipu_dc_priv *dc_priv;
struct ipu_dp_priv *dp_priv;
struct ipu_dmfc_priv *dmfc_priv;
struct ipu_di *di_priv[2];
};
void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
int ipu_di_init(struct ipu_soc *ipu, struct device_d *dev, int id,
void __iomem *base, u32 module, struct clk *ipu_clk);
void ipu_di_exit(struct ipu_soc *ipu, int id);
int ipu_dmfc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base,
struct clk *ipu_clk);
void ipu_dmfc_exit(struct ipu_soc *ipu);
int ipu_dp_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base);
void ipu_dp_exit(struct ipu_soc *ipu);
int ipu_dc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base,
void __iomem *template_base);
void ipu_dc_exit(struct ipu_soc *ipu);
int ipu_cpmem_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base);
void ipu_cpmem_exit(struct ipu_soc *ipu);
#endif /* __IPU_PRV_H__ */

View File

@ -0,0 +1,353 @@
/*
* Freescale i.MX Frame Buffer device driver
*
* Copyright (C) 2004 Sascha Hauer, Pengutronix
* Based on acornfb.c Copyright (C) Russell King.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*
*/
#define pr_fmt(fmt) "IPU: " fmt
#include <common.h>
#include <fb.h>
#include <io.h>
#include <driver.h>
#include <malloc.h>
#include <errno.h>
#include <init.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <asm-generic/div64.h>
#include <asm/mmu.h>
#include "imx-ipu-v3.h"
#include "ipuv3-plane.h"
/*
* These are the bitfields for each
* display depth that we support.
*/
struct ipufb_rgb {
struct fb_bitfield red;
struct fb_bitfield green;
struct fb_bitfield blue;
struct fb_bitfield transp;
};
struct ipufb_info {
void __iomem *regs;
struct clk *ahb_clk;
struct clk *ipg_clk;
struct clk *per_clk;
struct fb_videomode *mode;
struct fb_info info;
struct device_d *dev;
/* plane[0] is the full plane, plane[1] is the partial plane */
struct ipu_plane *plane[2];
void (*enable)(int enable);
unsigned int di_clkflags;
u32 interface_pix_fmt;
struct ipu_dc *dc;
struct ipu_di *di;
struct list_head list;
char *name;
int id;
struct ipu_output *output;
};
static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
static LIST_HEAD(ipu_outputs);
static LIST_HEAD(ipu_fbs);
int ipu_register_output(struct ipu_output *ouput)
{
list_add_tail(&ouput->list, &ipu_outputs);
return 0;
}
static int ipu_register_fb(struct ipufb_info *ipufb)
{
list_add_tail(&ipufb->list, &ipu_fbs);
return 0;
}
int ipu_crtc_mode_set(struct ipufb_info *fbi,
struct fb_videomode *mode,
int x, int y)
{
struct fb_info *info = &fbi->info;
int ret;
struct ipu_di_signal_cfg sig_cfg = {};
u32 out_pixel_fmt;
dev_info(fbi->dev, "%s: mode->xres: %d\n", __func__,
mode->xres);
dev_info(fbi->dev, "%s: mode->yres: %d\n", __func__,
mode->yres);
out_pixel_fmt = fbi->output->out_pixel_fmt;
if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
sig_cfg.Hsync_pol = 1;
if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
sig_cfg.Vsync_pol = 1;
sig_cfg.enable_pol = 1;
sig_cfg.clk_pol = 1;
sig_cfg.width = mode->xres;
sig_cfg.height = mode->yres;
sig_cfg.pixel_fmt = out_pixel_fmt;
sig_cfg.h_start_width = mode->left_margin;
sig_cfg.h_sync_width = mode->hsync_len;
sig_cfg.h_end_width = mode->right_margin;
sig_cfg.v_start_width = mode->upper_margin;
sig_cfg.v_sync_width = mode->vsync_len;
sig_cfg.v_end_width = mode->lower_margin;
sig_cfg.pixelclock = PICOS2KHZ(mode->pixclock) * 1000UL;
sig_cfg.clkflags = fbi->output->di_clkflags;
sig_cfg.v_to_h_sync = 0;
sig_cfg.hsync_pin = 2;
sig_cfg.vsync_pin = 3;
ret = ipu_dc_init_sync(fbi->dc, fbi->di, sig_cfg.interlaced,
out_pixel_fmt, mode->xres);
if (ret) {
dev_err(fbi->dev,
"initializing display controller failed with %d\n",
ret);
return ret;
}
ret = ipu_di_init_sync_panel(fbi->di, &sig_cfg);
if (ret) {
dev_err(fbi->dev,
"initializing panel failed with %d\n", ret);
return ret;
}
ret = ipu_plane_mode_set(fbi->plane[0], mode, info, DRM_FORMAT_XRGB8888,
0, 0, mode->xres, mode->yres,
x, y, mode->xres, mode->yres);
if (ret) {
dev_err(fbi->dev, "initialising plane failed with %s\n", strerror(-ret));
return ret;
}
ret = ipu_di_enable(fbi->di);
if (ret) {
dev_err(fbi->dev, "enabling di failed with %s\n", strerror(-ret));
return ret;
}
ipu_dc_enable_channel(fbi->dc);
ipu_plane_enable(fbi->plane[0]);
return 0;
}
static void ipufb_enable_controller(struct fb_info *info)
{
struct ipufb_info *fbi = container_of(info, struct ipufb_info, info);
struct ipu_output *output = fbi->output;
if (output->ops->prepare)
output->ops->prepare(output, info->mode, fbi->id);
ipu_crtc_mode_set(fbi, info->mode, 0, 0);
if (output->ops->enable)
output->ops->enable(output, info->mode, fbi->id);
}
static void ipufb_disable_controller(struct fb_info *info)
{
struct ipufb_info *fbi = container_of(info, struct ipufb_info, info);
struct ipu_output *output = fbi->output;
if (output->ops->disable)
output->ops->disable(output);
ipu_plane_disable(fbi->plane[0]);
ipu_dc_disable_channel(fbi->dc);
ipu_di_disable(fbi->di);
if (output->ops->unprepare)
output->ops->unprepare(output);
}
static int ipufb_activate_var(struct fb_info *info)
{
struct ipufb_info *fbi = container_of(info, struct ipufb_info, info);
info->line_length = info->xres * (info->bits_per_pixel >> 3);
fbi->info.screen_base = dma_alloc_coherent(info->line_length * info->yres);
if (!fbi->info.screen_base)
return -ENOMEM;
memset(fbi->info.screen_base, 0, info->line_length * info->yres);
return 0;
}
static struct fb_ops ipufb_ops = {
.fb_enable = ipufb_enable_controller,
.fb_disable = ipufb_disable_controller,
.fb_activate_var = ipufb_activate_var,
};
static struct ipufb_info *ipu_output_find_di(struct ipu_output *output)
{
struct ipufb_info *ipufb;
list_for_each_entry(ipufb, &ipu_fbs, list) {
if (!(output->ipu_mask & (1 << ipufb->id)))
continue;
if (ipufb->output)
continue;
return ipufb;
}
return NULL;
}
static int ipu_init(void)
{
struct ipu_output *output;
struct ipufb_info *ipufb;
int ret;
list_for_each_entry(output, &ipu_outputs, list) {
pr_info("found output: %s\n", output->name);
ipufb = ipu_output_find_di(output);
if (!ipufb) {
pr_info("no di found for output %s\n", output->name);
continue;
}
pr_info("using di %s for output %s\n", ipufb->name, output->name);
ipufb->output = output;
ipufb->info.edid_i2c_adapter = output->edid_i2c_adapter;
if (output->modes) {
ipufb->info.modes.modes = output->modes->modes;
ipufb->info.modes.num_modes = output->modes->num_modes;
}
ret = register_framebuffer(&ipufb->info);
if (ret < 0) {
dev_err(ipufb->dev, "failed to register framebuffer\n");
return ret;
}
}
return 0;
}
late_initcall(ipu_init);
static int ipu_get_resources(struct ipufb_info *fbi,
struct ipu_client_platformdata *pdata)
{
struct ipu_soc *ipu = fbi->dev->parent->priv;
int ret;
int dp = -EINVAL;
fbi->dc = ipu_dc_get(ipu, pdata->dc);
if (IS_ERR(fbi->dc)) {
ret = PTR_ERR(fbi->dc);
goto err_out;
}
fbi->di = ipu_di_get(ipu, pdata->di);
if (IS_ERR(fbi->di)) {
ret = PTR_ERR(fbi->di);
goto err_out;
}
if (pdata->dp >= 0)
dp = IPU_DP_FLOW_SYNC_BG;
fbi->plane[0] = ipu_plane_init(ipu, pdata->dma[0], dp);
ret = ipu_plane_get_resources(fbi->plane[0]);
if (ret) {
dev_err(fbi->dev, "getting plane 0 resources failed with %d.\n",
ret);
goto err_out;
}
return 0;
err_out:
return ret;
}
static int ipufb_probe(struct device_d *dev)
{
struct ipufb_info *fbi;
struct fb_info *info;
int ret, ipuid;
struct ipu_client_platformdata *pdata = dev->platform_data;
struct ipu_rgb *ipu_rgb;
fbi = xzalloc(sizeof(*fbi));
info = &fbi->info;
ipuid = of_alias_get_id(dev->parent->device_node, "ipu");
fbi->name = asprintf("ipu%d-di%d", ipuid + 1, pdata->di);
fbi->id = ipuid * 2 + pdata->di;
fbi->dev = dev;
info->priv = fbi;
info->fbops = &ipufb_ops;
ipu_rgb = drm_fourcc_to_rgb(DRM_FORMAT_RGB888);
info->bits_per_pixel = 32;
info->red = ipu_rgb->red;
info->green = ipu_rgb->green;
info->blue = ipu_rgb->blue;
info->transp = ipu_rgb->transp;
dev_dbg(dev, "i.MX Framebuffer driver\n");
ret = ipu_get_resources(fbi, pdata);
if (ret)
return ret;
ret = ipu_register_fb(fbi);
return ret;
}
static void ipufb_remove(struct device_d *dev)
{
}
static struct driver_d ipufb_driver = {
.name = "imx-ipuv3-crtc",
.probe = ipufb_probe,
.remove = ipufb_remove,
};
device_platform_driver(ipufb_driver);

View File

@ -0,0 +1,239 @@
/*
* i.MX IPUv3 DP Overlay Planes
*
* Copyright (C) 2013 Philipp Zabel, Pengutronix
*
* 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.
*/
#include <common.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <malloc.h>
#include <fb.h>
#include "ipuv3-plane.h"
#include "imx-ipu-v3.h"
static int calc_vref(struct fb_videomode *mode)
{
unsigned long htotal, vtotal;
htotal = mode->left_margin + mode->right_margin + mode->xres + mode->hsync_len;
vtotal = mode->upper_margin + mode->lower_margin + mode->yres + mode->vsync_len;
return DIV_ROUND_UP(PICOS2KHZ(mode->pixclock) * 1000, vtotal * htotal);
}
static inline int calc_bandwidth(int width, int height, unsigned int vref)
{
return width * height * vref;
}
int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct fb_info *info,
int x, int y)
{
struct ipu_ch_param __iomem *cpmem;
pr_debug("phys = 0x%p, x = %d, y = %d, line_length = %d\n",
info->screen_base, x, y, info->line_length);
cpmem = ipu_get_cpmem(ipu_plane->ipu_ch);
ipu_cpmem_set_stride(cpmem, info->line_length);
ipu_cpmem_set_buffer(cpmem, 0, (unsigned long)info->screen_base +
info->line_length * y + x);
ipu_cpmem_set_buffer(cpmem, 1, (unsigned long)info->screen_base +
info->line_length * y + x);
return 0;
}
int ipu_plane_mode_set(struct ipu_plane *ipu_plane,
struct fb_videomode *mode,
struct fb_info *info, u32 pixel_format,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
struct ipu_ch_param __iomem *cpmem;
struct device_d *dev = &info->dev;
int ret;
/* no scaling */
if (src_w != crtc_w || src_h != crtc_h)
return -EINVAL;
/* clip to crtc bounds */
if (crtc_x < 0) {
if (-crtc_x > crtc_w)
return -EINVAL;
src_x += -crtc_x;
src_w -= -crtc_x;
crtc_w -= -crtc_x;
crtc_x = 0;
}
if (crtc_y < 0) {
if (-crtc_y > crtc_h)
return -EINVAL;
src_y += -crtc_y;
src_h -= -crtc_y;
crtc_h -= -crtc_y;
crtc_y = 0;
}
if (crtc_x + crtc_w > mode->xres) {
if (crtc_x > mode->xres)
return -EINVAL;
crtc_w = mode->xres - crtc_x;
src_w = crtc_w;
}
if (crtc_y + crtc_h > mode->yres) {
if (crtc_y > mode->yres)
return -EINVAL;
crtc_h = mode->yres - crtc_y;
src_h = crtc_h;
}
/* full plane minimum width is 13 pixels */
if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG))
return -EINVAL;
if (crtc_h < 2)
return -EINVAL;
switch (ipu_plane->dp_flow) {
case IPU_DP_FLOW_SYNC_BG:
ret = ipu_dp_setup_channel(ipu_plane->dp,
IPUV3_COLORSPACE_RGB,
IPUV3_COLORSPACE_RGB);
if (ret) {
dev_err(dev,
"initializing display processor failed with %d\n",
ret);
return ret;
}
ipu_dp_set_global_alpha(ipu_plane->dp, 1, 0, 1);
break;
case IPU_DP_FLOW_SYNC_FG:
ipu_dp_setup_channel(ipu_plane->dp,
IPUV3_COLORSPACE_RGB,
IPUV3_COLORSPACE_UNKNOWN);
ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y);
break;
}
ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w);
if (ret) {
dev_err(dev, "initializing dmfc channel failed with %d\n", ret);
return ret;
}
ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
calc_bandwidth(crtc_w, crtc_h,
calc_vref(mode)), 64);
if (ret) {
dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret);
return ret;
}
cpmem = ipu_get_cpmem(ipu_plane->ipu_ch);
ipu_ch_param_zero(cpmem);
ipu_cpmem_set_resolution(cpmem, src_w, src_h);
ret = ipu_cpmem_set_fmt(cpmem, pixel_format);
if (ret < 0) {
dev_err(dev, "unsupported pixel format 0x%08x\n",
pixel_format);
return ret;
}
ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
ret = ipu_plane_set_base(ipu_plane, info, src_x, src_y);
if (ret < 0)
return ret;
return 0;
}
void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
{
if (!IS_ERR_OR_NULL(ipu_plane->dp))
ipu_dp_put(ipu_plane->dp);
if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
ipu_dmfc_put(ipu_plane->dmfc);
if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
ipu_idmac_put(ipu_plane->ipu_ch);
}
int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
{
int ret;
ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
if (IS_ERR(ipu_plane->ipu_ch)) {
ret = PTR_ERR(ipu_plane->ipu_ch);
pr_err("failed to get idmac channel: %d\n", ret);
return ret;
}
ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
if (IS_ERR(ipu_plane->dmfc)) {
ret = PTR_ERR(ipu_plane->dmfc);
pr_err("failed to get dmfc: ret %d\n", ret);
goto err_out;
}
if (ipu_plane->dp_flow >= 0) {
ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
if (IS_ERR(ipu_plane->dp)) {
ret = PTR_ERR(ipu_plane->dp);
pr_err("failed to get dp flow: %d\n", ret);
goto err_out;
}
}
return 0;
err_out:
ipu_plane_put_resources(ipu_plane);
return ret;
}
void ipu_plane_enable(struct ipu_plane *ipu_plane)
{
ipu_dmfc_enable_channel(ipu_plane->dmfc);
ipu_idmac_enable_channel(ipu_plane->ipu_ch);
if (ipu_plane->dp)
ipu_dp_enable_channel(ipu_plane->dp);
ipu_plane->enabled = true;
}
void ipu_plane_disable(struct ipu_plane *ipu_plane)
{
ipu_plane->enabled = false;
ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
if (ipu_plane->dp)
ipu_dp_disable_channel(ipu_plane->dp);
ipu_idmac_disable_channel(ipu_plane->ipu_ch);
ipu_dmfc_disable_channel(ipu_plane->dmfc);
}
struct ipu_plane *ipu_plane_init(struct ipu_soc *ipu,
int dma, int dp)
{
struct ipu_plane *ipu_plane;
ipu_plane = xzalloc(sizeof(*ipu_plane));
ipu_plane->ipu = ipu;
ipu_plane->dma = dma;
ipu_plane->dp_flow = dp;
return ipu_plane;
}

View File

@ -0,0 +1,51 @@
#ifndef __IPUV3_PLANE_H__
#define __IPUV3_PLANE_H__
struct drm_plane;
struct drm_device;
struct ipu_soc;
struct drm_crtc;
struct drm_framebuffer;
struct ipuv3_channel;
struct dmfc_channel;
struct ipu_dp;
struct ipu_plane {
struct ipu_soc *ipu;
struct ipuv3_channel *ipu_ch;
struct dmfc_channel *dmfc;
struct ipu_dp *dp;
int dma;
int dp_flow;
int x;
int y;
bool enabled;
};
struct ipu_plane *ipu_plane_init(struct ipu_soc *ipu,
int dma, int dp);
/* Init IDMAC, DMFC, DP */
int ipu_plane_mode_set(struct ipu_plane *ipu_plane,
struct fb_videomode *mode,
struct fb_info *info, u32 pixel_format,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h);
void ipu_plane_enable(struct ipu_plane *plane);
void ipu_plane_disable(struct ipu_plane *plane);
int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct fb_info *info,
int x, int y);
int ipu_plane_get_resources(struct ipu_plane *plane);
void ipu_plane_put_resources(struct ipu_plane *plane);
int ipu_plane_irq(struct ipu_plane *plane);
#endif

View File

@ -294,7 +294,7 @@ static int imxfb_activate_var(struct fb_info *info)
u32 pcr;
int i;
for (i = 0; i < info->num_modes; i++) {
for (i = 0; i < info->modes.num_modes; i++) {
if (!strcmp(fbi->mode[i].mode.name, mode->name)) {
fbi->pcr = fbi->mode[i].pcr;
break;
@ -551,8 +551,8 @@ static int imxfb_probe(struct device_d *dev)
fbi->enable = pdata->enable;
fbi->dev = dev;
info->priv = fbi;
info->mode_list = mode_list;
info->num_modes = pdata->num_modes;
info->modes.modes = mode_list;
info->modes.num_modes = pdata->num_modes;
info->mode = &pdata->mode->mode;
info->xres = pdata->mode->mode.xres;
info->yres = pdata->mode->mode.yres;

View File

@ -0,0 +1,238 @@
/*
* OF helpers for parsing display timings
*
* Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
*
* based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
*
* This file is released under the GPLv2
*/
#include <common.h>
#include <of.h>
#include <fb.h>
#include <malloc.h>
void display_timings_release(struct display_timings *disp)
{
free(disp->modes);
free(disp);
}
EXPORT_SYMBOL_GPL(display_timings_release);
/**
* parse_timing_property - parse timing_entry from device_node
* @np: device_node with the property
* @name: name of the property
* @result: will be set to the return value
*
* DESCRIPTION:
* Every display_timing can be specified with either just the typical value or
* a range consisting of min/typ/max. This function helps handling this
**/
static int parse_timing_property(const struct device_node *np, const char *name,
u32 *res)
{
struct property *prop;
int length, cells, ret;
prop = of_find_property(np, name, &length);
if (!prop) {
pr_err("%s: could not find property %s\n",
np->full_name, name);
return -EINVAL;
}
cells = length / sizeof(u32);
if (cells == 1) {
ret = of_property_read_u32(np, name, res);
} else {
pr_err("%s: illegal timing specification in %s\n",
np->full_name, name);
return -EINVAL;
}
return ret;
}
/**
* of_parse_display_timing - parse display_timing entry from device_node
* @np: device_node with the properties
**/
static int of_parse_display_timing(const struct device_node *np,
struct fb_videomode *mode)
{
u32 val = 0, pixelclock = 0;
int ret = 0;
memset(mode, 0, sizeof(*mode));
ret |= parse_timing_property(np, "hback-porch", &mode->left_margin);
ret |= parse_timing_property(np, "hfront-porch", &mode->right_margin);
ret |= parse_timing_property(np, "hactive", &mode->xres);
ret |= parse_timing_property(np, "hsync-len", &mode->hsync_len);
ret |= parse_timing_property(np, "vback-porch", &mode->upper_margin);
ret |= parse_timing_property(np, "vfront-porch", &mode->lower_margin);
ret |= parse_timing_property(np, "vactive", &mode->yres);
ret |= parse_timing_property(np, "vsync-len", &mode->vsync_len);
ret |= parse_timing_property(np, "clock-frequency", &pixelclock);
mode->pixclock = pixelclock ? KHZ2PICOS(pixelclock / 1000) : 0;
if (!of_property_read_u32(np, "vsync-active", &val))
mode->sync |= val ? FB_SYNC_VERT_HIGH_ACT : 0;
if (!of_property_read_u32(np, "hsync-active", &val))
mode->sync |= val ? FB_SYNC_HOR_HIGH_ACT : 0;
if (!of_property_read_u32(np, "de-active", &val))
mode->display_flags |= val ? DISPLAY_FLAGS_DE_HIGH :
DISPLAY_FLAGS_DE_LOW;
if (!of_property_read_u32(np, "pixelclk-active", &val))
mode->display_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
DISPLAY_FLAGS_PIXDATA_NEGEDGE;
if (ret) {
pr_err("%s: error reading timing properties\n",
np->full_name);
return -EINVAL;
}
return 0;
}
/**
* of_get_display_timing - parse a display_timing entry
* @np: device_node with the timing subnode
* @name: name of the timing node
* @dt: display_timing struct to fill
**/
int of_get_display_timing(struct device_node *np, const char *name,
struct fb_videomode *mode)
{
struct device_node *timing_np;
if (!np) {
pr_err("%s: no devicenode given\n", np->full_name);
return -EINVAL;
}
timing_np = of_get_child_by_name(np, name);
if (!timing_np) {
pr_err("%s: could not find node '%s'\n",
np->full_name, name);
return -ENOENT;
}
return of_parse_display_timing(timing_np, mode);
}
EXPORT_SYMBOL_GPL(of_get_display_timing);
/**
* of_get_display_timings - parse all display_timing entries from a device_node
* @np: device_node with the subnodes
**/
struct display_timings *of_get_display_timings(struct device_node *np)
{
struct device_node *timings_np;
struct device_node *entry;
struct device_node *native_mode;
struct display_timings *disp;
if (!np) {
pr_err("%s: no device node given\n", np->full_name);
return NULL;
}
timings_np = of_get_child_by_name(np, "display-timings");
if (!timings_np) {
pr_debug("%s: could not find display-timings node\n",
np->full_name);
return NULL;
}
disp = xzalloc(sizeof(*disp));
entry = of_parse_phandle(timings_np, "native-mode", 0);
/* assume first child as native mode if none provided */
if (!entry)
entry = of_get_next_available_child(np, NULL);
/* if there is no child, it is useless to go on */
if (!entry) {
pr_err("%s: no timing specifications given\n",
np->full_name);
goto entryfail;
}
pr_debug("%s: using %s as default timing\n",
np->full_name, entry->name);
native_mode = entry;
disp->num_modes = of_get_child_count(timings_np);
if (disp->num_modes == 0) {
/* should never happen, as entry was already found above */
pr_err("%s: no timings specified\n", np->full_name);
goto entryfail;
}
disp->modes = xzalloc(sizeof(struct fb_videomode) * disp->num_modes);
disp->num_modes = 0;
disp->native_mode = 0;
for_each_child_of_node(timings_np, entry) {
struct fb_videomode *mode;
int r;
mode = &disp->modes[disp->num_modes];
r = of_parse_display_timing(entry, mode);
if (r) {
/*
* to not encourage wrong devicetrees, fail in case of
* an error
*/
pr_err("%s: error in timing %d\n",
np->full_name, disp->num_modes + 1);
goto timingfail;
}
mode->name = xstrdup(entry->name);
if (native_mode == entry)
disp->native_mode = disp->num_modes;
disp->num_modes++;
}
pr_debug("%s: got %d timings. Using timing #%d as default\n",
np->full_name, disp->num_modes,
disp->native_mode + 1);
return disp;
timingfail:
display_timings_release(disp);
entryfail:
free(disp);
return NULL;
}
EXPORT_SYMBOL_GPL(of_get_display_timings);
/**
* of_display_timings_exist - check if a display-timings node is provided
* @np: device_node with the timing
**/
int of_display_timings_exist(struct device_node *np)
{
struct device_node *timings_np;
if (!np)
return -EINVAL;
timings_np = of_parse_phandle(np, "display-timings", 0);
if (!timings_np)
return -EINVAL;
return 1;
}
EXPORT_SYMBOL_GPL(of_display_timings_exist);

View File

@ -467,8 +467,8 @@ static int omapfb_probe(struct device_d *dev)
for (i = 0; i < pdata->num_displays; ++i)
fbi->video_modes[i] = pdata->displays[i].mode;
info->mode_list = fbi->video_modes;
info->num_modes = pdata->num_displays;
info->modes.modes = fbi->video_modes;
info->modes.num_modes = pdata->num_displays;
info->priv = fbi;
info->fbops = &omapfb_ops;

View File

@ -376,9 +376,9 @@ static int s3cfb_probe(struct device_d *hw_dev)
hw_dev->priv = &fbi;
/* add runtime video info */
fbi.info.mode_list = pdata->mode_list;
fbi.info.num_modes = pdata->mode_cnt;
fbi.info.mode = &fbi.info.mode_list[1];
fbi.info.modes.modes = pdata->mode_list;
fbi.info.modes.num_modes = pdata->mode_cnt;
fbi.info.mode = &fbi.info.modes.modes[0];
fbi.info.xres = fbi.info.mode->xres;
fbi.info.yres = fbi.info.mode->yres;
if (pdata->bits_per_pixel)

View File

@ -37,8 +37,8 @@ static int sdlfb_probe(struct device_d *dev)
return -EIO;
fb = xzalloc(sizeof(*fb));
fb->mode_list = fb->mode = dev->platform_data;
fb->num_modes = 1;
fb->modes.modes = fb->mode = dev->platform_data;
fb->modes.num_modes = 1;
fb->bits_per_pixel = 4 << 3;
fb->xres = fb->mode->xres;
fb->yres = fb->mode->yres;

View File

@ -499,9 +499,9 @@ static int stmfb_probe(struct device_d *hw_dev)
clk_enable(fbi.clk);
/* add runtime video info */
fbi.info.mode_list = pdata->mode_list;
fbi.info.num_modes = pdata->mode_cnt;
fbi.info.mode = &fbi.info.mode_list[0];
fbi.info.modes.modes = pdata->mode_list;
fbi.info.modes.num_modes = pdata->mode_cnt;
fbi.info.mode = &fbi.info.modes.modes[0];
fbi.info.xres = fbi.info.mode->xres;
fbi.info.yres = fbi.info.mode->yres;
if (pdata->bits_per_pixel)

View File

@ -143,3 +143,8 @@ pblx-$(CONFIG_MACH_UDOO) += start_imx6_udoo
CFG_start_imx6_udoo.pblx.imximg = $(board)/udoo/flash-header-mx6-udoo.imxcfg
FILE_barebox-udoo-imx6q.img = start_imx6_udoo.pblx.imximg
image-$(CONFIG_MACH_UDOO) += barebox-udoo-imx6q.img
pblx-$(CONFIG_MACH_VARISCITE_MX6) += start_variscite_custom
CFG_start_variscite_custom.pblx.imximg = $(board)/variscite-mx6/flash-header-variscite.imxcfg
FILE_barebox-variscite-custom.img = start_variscite_custom.pblx.imximg
image-$(CONFIG_MACH_VARISCITE_MX6) += barebox-variscite-custom.img

View File

@ -4,6 +4,7 @@
#include <ioctl.h>
#include <param.h>
#include <driver.h>
#include <linux/bitops.h>
#define FB_VISUAL_TRUECOLOR 2 /* True color */
#define FB_VISUAL_PSEUDOCOLOR 3 /* Pseudo color (like atari) */
@ -32,6 +33,16 @@
#define PICOS2KHZ(a) (1000000000UL/(a))
#define KHZ2PICOS(a) (1000000000UL/(a))
enum display_flags {
/* data enable flag */
DISPLAY_FLAGS_DE_LOW = BIT(4),
DISPLAY_FLAGS_DE_HIGH = BIT(5),
/* drive data on pos. edge */
DISPLAY_FLAGS_PIXDATA_POSEDGE = BIT(6),
/* drive data on neg. edge */
DISPLAY_FLAGS_PIXDATA_NEGEDGE = BIT(7),
};
struct fb_videomode {
const char *name; /* optional */
u32 refresh; /* optional */
@ -46,7 +57,7 @@ struct fb_videomode {
u32 vsync_len;
u32 sync;
u32 vmode;
u32 flag;
u32 display_flags;
};
/* Interpretation of offset for color fields: All offsets are from the right,
@ -77,10 +88,30 @@ struct fb_ops {
int (*fb_activate_var)(struct fb_info *info);
};
/*
* This describes all timing settings a display provides.
* The native_mode is the default setting for this display.
* Drivers that can handle multiple videomodes should work with this struct and
* convert each entry to the desired end result.
*/
struct display_timings {
unsigned int native_mode;
unsigned int num_modes;
struct fb_videomode *modes;
};
struct i2c_adapter;
struct fb_info {
struct fb_videomode *mode;
struct fb_videomode *mode_list;
unsigned num_modes;
struct display_timings modes;
int current_mode;
void *edid_data;
struct i2c_adapter *edid_i2c_adapter;
struct display_timings edid_modes;
struct fb_ops *fbops;
struct device_d dev; /* This is this fb device */
@ -111,6 +142,8 @@ struct fb_info {
*/
};
struct display_timings *of_get_display_timings(struct device_node *np);
int register_framebuffer(struct fb_info *info);
#define FBIOGET_SCREENINFO _IOR('F', 1, loff_t)
@ -123,5 +156,8 @@ extern struct bus_type fb_bus;
int fb_register_simplefb(struct fb_info *info);
#endif /* __FB_H */
int edid_to_display_timings(struct display_timings *, unsigned char *edid);
void *edid_read_i2c(struct i2c_adapter *adapter);
void fb_edid_add_modes(struct fb_info *info);
#endif /* __FB_H */

View File

@ -133,6 +133,7 @@ static inline int i2c_register_board_info(int busnum,
#endif
extern int i2c_add_numbered_adapter(struct i2c_adapter *adapter);
struct i2c_adapter *i2c_get_adapter(int busnum);
struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node);
/* For devices that use several addresses, use i2c_new_dummy() to make
* client handles for the extra addresses.

View File

@ -196,6 +196,9 @@ static inline int clk_set_rate(struct clk *clk, unsigned long rate)
#endif
#ifdef CONFIG_COMMON_CLK
#define CLK_SET_RATE_PARENT (1 << 0) /* propagate rate change up one level */
struct clk_ops {
int (*enable)(struct clk *clk);
void (*disable)(struct clk *clk);
@ -237,36 +240,46 @@ struct clk_divider {
const char *parent;
#define CLK_DIVIDER_ONE_BASED (1 << 0)
unsigned flags;
const struct clk_div_table *table;
int max_div_index;
int table_size;
};
extern struct clk_ops clk_divider_ops;
struct clk *clk_divider(const char *name, const char *parent,
void __iomem *reg, u8 shift, u8 width);
void __iomem *reg, u8 shift, u8 width, unsigned flags);
struct clk *clk_divider_one_based(const char *name, const char *parent,
void __iomem *reg, u8 shift, u8 width);
void __iomem *reg, u8 shift, u8 width, unsigned flags);
struct clk *clk_divider_table(const char *name,
const char *parent, void __iomem *reg, u8 shift, u8 width,
const struct clk_div_table *table);
const struct clk_div_table *table, unsigned flags);
struct clk *clk_fixed_factor(const char *name,
const char *parent, unsigned int mult, unsigned int div);
const char *parent, unsigned int mult, unsigned int div,
unsigned flags);
struct clk *clk_mux_alloc(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, u8 num_parents);
u8 shift, u8 width, const char **parents, u8 num_parents,
unsigned flags);
void clk_mux_free(struct clk *clk_mux);
struct clk *clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, u8 num_parents);
u8 shift, u8 width, const char **parents, u8 num_parents,
unsigned flags);
struct clk *clk_gate_alloc(const char *name, const char *parent,
void __iomem *reg, u8 shift);
void __iomem *reg, u8 shift, unsigned flags);
void clk_gate_free(struct clk *clk_gate);
struct clk *clk_gate(const char *name, const char *parent, void __iomem *reg,
u8 shift);
u8 shift, unsigned flags);
struct clk *clk_gate_inverted(const char *name, const char *parent, void __iomem *reg,
u8 shift);
u8 shift, unsigned flags);
int clk_is_enabled(struct clk *clk);
int clk_is_enabled_always(struct clk *clk);
long clk_parent_round_rate(struct clk *clk, unsigned long rate,
unsigned long *prate);
int clk_parent_set_rate(struct clk *clk, unsigned long rate,
unsigned long parent_rate);
int clk_register(struct clk *clk);

View File

@ -52,6 +52,14 @@ static inline void *ERR_CAST(const void *ptr)
return (void *) ptr;
}
static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr)
{
if (IS_ERR(ptr))
return PTR_ERR(ptr);
else
return 0;
}
#endif
#endif /* _LINUX_ERR_H */

View File

@ -241,6 +241,24 @@
#define IMX6Q_GPR5_L2_CLK_STOP BIT(8)
#define IMX6Q_GPR6_IPU1_ID00_WR_QOS_MASK (0xf << 0)
#define IMX6Q_GPR6_IPU1_ID01_WR_QOS_MASK (0xf << 4)
#define IMX6Q_GPR6_IPU1_ID10_WR_QOS_MASK (0xf << 8)
#define IMX6Q_GPR6_IPU1_ID11_WR_QOS_MASK (0xf << 12)
#define IMX6Q_GPR6_IPU1_ID00_RD_QOS_MASK (0xf << 16)
#define IMX6Q_GPR6_IPU1_ID01_RD_QOS_MASK (0xf << 20)
#define IMX6Q_GPR6_IPU1_ID10_RD_QOS_MASK (0xf << 24)
#define IMX6Q_GPR6_IPU1_ID11_RD_QOS_MASK (0xf << 28)
#define IMX6Q_GPR7_IPU2_ID00_WR_QOS_MASK (0xf << 0)
#define IMX6Q_GPR7_IPU2_ID01_WR_QOS_MASK (0xf << 4)
#define IMX6Q_GPR7_IPU2_ID10_WR_QOS_MASK (0xf << 8)
#define IMX6Q_GPR7_IPU2_ID11_WR_QOS_MASK (0xf << 12)
#define IMX6Q_GPR7_IPU2_ID00_RD_QOS_MASK (0xf << 16)
#define IMX6Q_GPR7_IPU2_ID01_RD_QOS_MASK (0xf << 20)
#define IMX6Q_GPR7_IPU2_ID10_RD_QOS_MASK (0xf << 24)
#define IMX6Q_GPR7_IPU2_ID11_RD_QOS_MASK (0xf << 28)
#define IMX6Q_GPR9_TZASC2_BYP BIT(1)
#define IMX6Q_GPR9_TZASC1_BYP BIT(0)

261
include/video/fourcc.h Normal file
View File

@ -0,0 +1,261 @@
#ifndef __VIDEO_FOURCC_H
#define __VIDEO_FOURCC_H
/* Four-character-code (FOURCC) */
#define v4l2_fourcc(a, b, c, d)\
((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))
/* Pixel format FOURCC depth Description */
/* RGB formats */
#define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R', 'G', 'B', '1') /* 8 RGB-3-3-2 */
#define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R', '4', '4', '4') /* 16 xxxxrrrr ggggbbbb */
#define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R', 'G', 'B', 'O') /* 16 RGB-5-5-5 */
#define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */
#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */
#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */
#define V4L2_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', 'H') /* 18 BGR-6-6-6 */
#define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */
#define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */
#define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */
#define V4L2_PIX_FMT_RGB32 v4l2_fourcc('R', 'G', 'B', '4') /* 32 RGB-8-8-8-8 */
/* Grey formats */
#define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */
#define V4L2_PIX_FMT_Y4 v4l2_fourcc('Y', '0', '4', ' ') /* 4 Greyscale */
#define V4L2_PIX_FMT_Y6 v4l2_fourcc('Y', '0', '6', ' ') /* 6 Greyscale */
#define V4L2_PIX_FMT_Y10 v4l2_fourcc('Y', '1', '0', ' ') /* 10 Greyscale */
#define V4L2_PIX_FMT_Y12 v4l2_fourcc('Y', '1', '2', ' ') /* 12 Greyscale */
#define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y', '1', '6', ' ') /* 16 Greyscale */
/* Grey bit-packed formats */
#define V4L2_PIX_FMT_Y10BPACK v4l2_fourcc('Y', '1', '0', 'B') /* 10 Greyscale bit-packed */
/* Palette formats */
#define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P', 'A', 'L', '8') /* 8 8-bit palette */
/* Chrominance formats */
#define V4L2_PIX_FMT_UV8 v4l2_fourcc('U', 'V', '8', ' ') /* 8 UV 4:4 */
/* Luminance+Chrominance formats */
#define V4L2_PIX_FMT_YVU410 v4l2_fourcc('Y', 'V', 'U', '9') /* 9 YVU 4:1:0 */
#define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */
#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y', 'Y', 'U', 'V') /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */
#define V4L2_PIX_FMT_UYVY v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_VYUY v4l2_fourcc('V', 'Y', 'U', 'Y') /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4', '2', '2', 'P') /* 16 YVU422 planar */
#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4', '1', '1', 'P') /* 16 YVU411 planar */
#define V4L2_PIX_FMT_Y41P v4l2_fourcc('Y', '4', '1', 'P') /* 12 YUV 4:1:1 */
#define V4L2_PIX_FMT_YUV444 v4l2_fourcc('Y', '4', '4', '4') /* 16 xxxxyyyy uuuuvvvv */
#define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y', 'U', 'V', 'O') /* 16 YUV-5-5-5 */
#define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y', 'U', 'V', 'P') /* 16 YUV-5-6-5 */
#define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y', 'U', 'V', '4') /* 32 YUV-8-8-8-8 */
#define V4L2_PIX_FMT_YUV410 v4l2_fourcc('Y', 'U', 'V', '9') /* 9 YUV 4:1:0 */
#define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y', 'U', '1', '2') /* 12 YUV 4:2:0 */
#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* 8 8-bit color */
#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2') /* 8 YUV 4:2:0 16x16 macroblocks */
#define V4L2_PIX_FMT_M420 v4l2_fourcc('M', '4', '2', '0') /* 12 YUV 4:2:0 2 lines y, 1 line uv interleaved */
/* two planes -- one Y, one Cr + Cb interleaved */
#define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */
#define V4L2_PIX_FMT_NV21 v4l2_fourcc('N', 'V', '2', '1') /* 12 Y/CrCb 4:2:0 */
#define V4L2_PIX_FMT_NV16 v4l2_fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */
#define V4L2_PIX_FMT_NV61 v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */
#define V4L2_PIX_FMT_NV24 v4l2_fourcc('N', 'V', '2', '4') /* 24 Y/CbCr 4:4:4 */
#define V4L2_PIX_FMT_NV42 v4l2_fourcc('N', 'V', '4', '2') /* 24 Y/CrCb 4:4:4 */
/* two non contiguous planes - one Y, one Cr + Cb interleaved */
#define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */
#define V4L2_PIX_FMT_NV21M v4l2_fourcc('N', 'M', '2', '1') /* 21 Y/CrCb 4:2:0 */
#define V4L2_PIX_FMT_NV16M v4l2_fourcc('N', 'M', '1', '6') /* 16 Y/CbCr 4:2:2 */
#define V4L2_PIX_FMT_NV61M v4l2_fourcc('N', 'M', '6', '1') /* 16 Y/CrCb 4:2:2 */
#define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 macroblocks */
#define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 macroblocks */
/* three non contiguous planes - Y, Cb, Cr */
#define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12 YUV420 planar */
#define V4L2_PIX_FMT_YVU420M v4l2_fourcc('Y', 'M', '2', '1') /* 12 YVU420 planar */
/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */
#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */
#define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') /* 8 GRGR.. BGBG.. */
#define V4L2_PIX_FMT_SRGGB8 v4l2_fourcc('R', 'G', 'G', 'B') /* 8 RGRG.. GBGB.. */
#define V4L2_PIX_FMT_SBGGR10 v4l2_fourcc('B', 'G', '1', '0') /* 10 BGBG.. GRGR.. */
#define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10 GBGB.. RGRG.. */
#define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10 GRGR.. BGBG.. */
#define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10 RGRG.. GBGB.. */
#define V4L2_PIX_FMT_SBGGR12 v4l2_fourcc('B', 'G', '1', '2') /* 12 BGBG.. GRGR.. */
#define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12 GBGB.. RGRG.. */
#define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */
#define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */
/* 10bit raw bayer a-law compressed to 8 bits */
#define V4L2_PIX_FMT_SBGGR10ALAW8 v4l2_fourcc('a', 'B', 'A', '8')
#define V4L2_PIX_FMT_SGBRG10ALAW8 v4l2_fourcc('a', 'G', 'A', '8')
#define V4L2_PIX_FMT_SGRBG10ALAW8 v4l2_fourcc('a', 'g', 'A', '8')
#define V4L2_PIX_FMT_SRGGB10ALAW8 v4l2_fourcc('a', 'R', 'A', '8')
/* 10bit raw bayer DPCM compressed to 8 bits */
#define V4L2_PIX_FMT_SBGGR10DPCM8 v4l2_fourcc('b', 'B', 'A', '8')
#define V4L2_PIX_FMT_SGBRG10DPCM8 v4l2_fourcc('b', 'G', 'A', '8')
#define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0')
#define V4L2_PIX_FMT_SRGGB10DPCM8 v4l2_fourcc('b', 'R', 'A', '8')
/*
* 10bit raw bayer, expanded to 16 bits
* xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb...
*/
#define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16 BGBG.. GRGR.. */
/* compressed formats */
#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG */
#define V4L2_PIX_FMT_JPEG v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG */
#define V4L2_PIX_FMT_DV v4l2_fourcc('d', 'v', 's', 'd') /* 1394 */
#define V4L2_PIX_FMT_MPEG v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 Multiplexed */
#define V4L2_PIX_FMT_H264 v4l2_fourcc('H', '2', '6', '4') /* H264 with start codes */
#define V4L2_PIX_FMT_H264_NO_SC v4l2_fourcc('A', 'V', 'C', '1') /* H264 without start codes */
#define V4L2_PIX_FMT_H264_MVC v4l2_fourcc('M', '2', '6', '4') /* H264 MVC */
#define V4L2_PIX_FMT_H263 v4l2_fourcc('H', '2', '6', '3') /* H263 */
#define V4L2_PIX_FMT_MPEG1 v4l2_fourcc('M', 'P', 'G', '1') /* MPEG-1 ES */
#define V4L2_PIX_FMT_MPEG2 v4l2_fourcc('M', 'P', 'G', '2') /* MPEG-2 ES */
#define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG-4 part 2 ES */
#define V4L2_PIX_FMT_XVID v4l2_fourcc('X', 'V', 'I', 'D') /* Xvid */
#define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
#define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
#define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0') /* VP8 */
/* Vendor-specific formats */
#define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
#define V4L2_PIX_FMT_WNVA v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */
#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */
#define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') /* SN9C20x YUV 4:2:0 */
#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P', 'W', 'C', '1') /* pwc older webcam */
#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */
#define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */
#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */
#define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S', '5', '0', '5') /* YYUV per line */
#define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */
#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */
#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */
#define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */
#define V4L2_PIX_FMT_JL2005BCD v4l2_fourcc('J', 'L', '2', '0') /* compressed RGGB bayer */
#define V4L2_PIX_FMT_SN9C2028 v4l2_fourcc('S', 'O', 'N', 'X') /* compressed GBRG bayer */
#define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */
#define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */
#define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */
#define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
#define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */
#define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */
#define V4L2_PIX_FMT_CIT_YYVYUY v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */
#define V4L2_PIX_FMT_KONICA420 v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */
#define V4L2_PIX_FMT_JPGL v4l2_fourcc('J', 'P', 'G', 'L') /* JPEG-Lite */
#define V4L2_PIX_FMT_SE401 v4l2_fourcc('S', '4', '0', '1') /* se401 janggu compressed rgb */
#define V4L2_PIX_FMT_S5C_UYVY_JPG v4l2_fourcc('S', '5', 'C', 'I') /* S5C73M3 interleaved UYVY/JPEG */
#define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \
((__u32)(c) << 16) | ((__u32)(d) << 24))
#define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */
/* color index */
#define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */
/* 8 bpp RGB */
#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */
#define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */
/* 16 bpp RGB */
#define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */
#define DRM_FORMAT_XBGR4444 fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */
#define DRM_FORMAT_RGBX4444 fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */
#define DRM_FORMAT_BGRX4444 fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */
#define DRM_FORMAT_ARGB4444 fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */
#define DRM_FORMAT_ABGR4444 fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */
#define DRM_FORMAT_RGBA4444 fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */
#define DRM_FORMAT_BGRA4444 fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */
#define DRM_FORMAT_XRGB1555 fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */
#define DRM_FORMAT_XBGR1555 fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */
#define DRM_FORMAT_RGBX5551 fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */
#define DRM_FORMAT_BGRX5551 fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */
#define DRM_FORMAT_ARGB1555 fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */
#define DRM_FORMAT_ABGR1555 fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */
#define DRM_FORMAT_RGBA5551 fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */
#define DRM_FORMAT_BGRA5551 fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */
#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
#define DRM_FORMAT_BGR565 fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */
/* 24 bpp RGB */
#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
/* 32 bpp RGB */
#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */
#define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */
#define DRM_FORMAT_RGBX8888 fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */
#define DRM_FORMAT_BGRX8888 fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */
#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */
#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
#define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */
#define DRM_FORMAT_XRGB2101010 fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */
#define DRM_FORMAT_XBGR2101010 fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */
#define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */
#define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */
#define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */
#define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */
#define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */
#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
/* packed YCbCr */
#define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */
#define DRM_FORMAT_YVYU fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */
#define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */
#define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */
#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */
/*
* 2 plane YCbCr
* index 0 = Y plane, [7:0] Y
* index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian
* or
* index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian
*/
#define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */
#define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */
#define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */
#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */
#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */
#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */
/* special NV12 tiled format */
#define DRM_FORMAT_NV12MT fourcc_code('T', 'M', '1', '2') /* 2x2 subsampled Cr:Cb plane 64x32 macroblocks */
/*
* 3 plane YCbCr
* index 0: Y plane, [7:0] Y
* index 1: Cb plane, [7:0] Cb
* index 2: Cr plane, [7:0] Cr
* or
* index 1: Cr plane, [7:0] Cr
* index 2: Cb plane, [7:0] Cb
*/
#define DRM_FORMAT_YUV410 fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */
#define DRM_FORMAT_YVU410 fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */
#define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */
#define DRM_FORMAT_YVU411 fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */
#define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */
#define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */
#define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */
#define DRM_FORMAT_YVU422 fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */
#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */
#define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */
#endif /* __VIDEO_FOURCC_H */