Merge branch 'for-next/imx-ipu-v3'
This commit is contained in:
commit
74b5a3dbb3
|
@ -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/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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>,
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
# ----------------------------------------------------------
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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__ */
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
@ -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__ */
|
|
@ -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");
|
|
@ -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, ®->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");
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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__ */
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
44
include/fb.h
44
include/fb.h
|
@ -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 */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 */
|
Loading…
Reference in New Issue