From b6578810718d34c3e57a95173bfec9f68e8ffb9b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 10 Dec 2013 15:22:13 +0100 Subject: [PATCH 01/29] err.h: Add PTR_ERR_OR_ZERO from kernel Signed-off-by: Sascha Hauer --- include/linux/err.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/err.h b/include/linux/err.h index 19fb70dc0..ed563f2c4 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -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 */ From 355c8611caa98f280f8ecbb5e96587c2ce8118e0 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 09:09:54 +0100 Subject: [PATCH 02/29] ARM: dts: i.MX6: Add IPU aliases Signed-off-by: Sascha Hauer --- arch/arm/dts/imx6q.dtsi | 1 + arch/arm/dts/imx6qdl.dtsi | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/dts/imx6q.dtsi b/arch/arm/dts/imx6q.dtsi index 52a9a2b37..f9378fad5 100644 --- a/arch/arm/dts/imx6q.dtsi +++ b/arch/arm/dts/imx6q.dtsi @@ -16,6 +16,7 @@ / { aliases { spi4 = &ecspi5; + ipu1 = &ipu2; }; cpus { diff --git a/arch/arm/dts/imx6qdl.dtsi b/arch/arm/dts/imx6qdl.dtsi index 70424d2cb..5160db4cf 100644 --- a/arch/arm/dts/imx6qdl.dtsi +++ b/arch/arm/dts/imx6qdl.dtsi @@ -45,6 +45,7 @@ spi3 = &ecspi4; usbphy0 = &usbphy1; usbphy1 = &usbphy2; + ipu0 = &ipu1; }; intc: interrupt-controller@00a01000 { From 6676cfcec6a1f74693b8509a22a57dd92b19ddde Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 09:10:21 +0100 Subject: [PATCH 03/29] ARM: dts: i.MX6: Add HDMI nodes Signed-off-by: Sascha Hauer --- arch/arm/dts/imx6q.dtsi | 4 ++++ arch/arm/dts/imx6qdl.dtsi | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/arch/arm/dts/imx6q.dtsi b/arch/arm/dts/imx6q.dtsi index f9378fad5..2b8ec43e0 100644 --- a/arch/arm/dts/imx6q.dtsi +++ b/arch/arm/dts/imx6q.dtsi @@ -158,6 +158,10 @@ }; }; +&hdmi { + compatible = "fsl,imx6q-hdmi"; +}; + &ldb { clocks = <&clks 33>, <&clks 34>, <&clks 39>, <&clks 40>, <&clks 41>, <&clks 42>, diff --git a/arch/arm/dts/imx6qdl.dtsi b/arch/arm/dts/imx6qdl.dtsi index 5160db4cf..8dfd7de48 100644 --- a/arch/arm/dts/imx6qdl.dtsi +++ b/arch/arm/dts/imx6qdl.dtsi @@ -665,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>; From b34eed098b9f2ce08a94980df95d5bf136dd4fa2 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 7 Mar 2014 08:20:12 +0100 Subject: [PATCH 04/29] ARM: i.MX6: Add initial variscite VAR-SOM-MX6 CPU support The VAR-SOM-MX6 is a System on module from Variscite, see: http://www.variscite.com/products/system-on-module-som/cortex-a9/var-som-mx6-cpu-freescale-imx6 This adds support for this module on the VAR-MX6CustomBoard baseboard. Signed-off-by: Sascha Hauer --- arch/arm/boards/Makefile | 1 + arch/arm/boards/variscite-mx6/Makefile | 3 + arch/arm/boards/variscite-mx6/board.c | 135 ++++++++++++++++ .../flash-header-variscite.imxcfg | 86 ++++++++++ arch/arm/boards/variscite-mx6/lowlevel.c | 74 +++++++++ arch/arm/dts/Makefile | 4 +- arch/arm/dts/imx6q-var-custom.dts | 149 ++++++++++++++++++ arch/arm/dts/imx6q-var-som.dtsi | 99 ++++++++++++ arch/arm/mach-imx/Kconfig | 6 + images/Makefile.imx | 5 + 10 files changed, 561 insertions(+), 1 deletion(-) create mode 100644 arch/arm/boards/variscite-mx6/Makefile create mode 100644 arch/arm/boards/variscite-mx6/board.c create mode 100644 arch/arm/boards/variscite-mx6/flash-header-variscite.imxcfg create mode 100644 arch/arm/boards/variscite-mx6/lowlevel.c create mode 100644 arch/arm/dts/imx6q-var-custom.dts create mode 100644 arch/arm/dts/imx6q-var-som.dtsi diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile index 0d8c9cf4c..ac85093d7 100644 --- a/arch/arm/boards/Makefile +++ b/arch/arm/boards/Makefile @@ -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/ diff --git a/arch/arm/boards/variscite-mx6/Makefile b/arch/arm/boards/variscite-mx6/Makefile new file mode 100644 index 000000000..35b114b8d --- /dev/null +++ b/arch/arm/boards/variscite-mx6/Makefile @@ -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 diff --git a/arch/arm/boards/variscite-mx6/board.c b/arch/arm/boards/variscite-mx6/board.c new file mode 100644 index 000000000..592b1116e --- /dev/null +++ b/arch/arm/boards/variscite-mx6/board.c @@ -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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/arch/arm/boards/variscite-mx6/flash-header-variscite.imxcfg b/arch/arm/boards/variscite-mx6/flash-header-variscite.imxcfg new file mode 100644 index 000000000..ed2105792 --- /dev/null +++ b/arch/arm/boards/variscite-mx6/flash-header-variscite.imxcfg @@ -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 diff --git a/arch/arm/boards/variscite-mx6/lowlevel.c b/arch/arm/boards/variscite-mx6/lowlevel.c new file mode 100644 index 000000000..e93e87484 --- /dev/null +++ b/arch/arm/boards/variscite-mx6/lowlevel.c @@ -0,0 +1,74 @@ +/* + * + * Copyright (C) 2013 Michael Burkey + * Based on code by Sascha Hauer + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 6bac3b908..d42fde9b7 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -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)) diff --git a/arch/arm/dts/imx6q-var-custom.dts b/arch/arm/dts/imx6q-var-custom.dts new file mode 100644 index 000000000..bb1193f9a --- /dev/null +++ b/arch/arm/dts/imx6q-var-custom.dts @@ -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 = ; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = ; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = ; + }; + + 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 = ; + }; + }; +}; + +&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>; +}; diff --git a/arch/arm/dts/imx6q-var-som.dtsi b/arch/arm/dts/imx6q-var-som.dtsi new file mode 100644 index 000000000..f423f2735 --- /dev/null +++ b/arch/arm/dts/imx6q-var-som.dtsi @@ -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 = ; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = ; + }; + }; +}; diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index b7e7533e8..b242716cf 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -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 # ---------------------------------------------------------- diff --git a/images/Makefile.imx b/images/Makefile.imx index 5b650de69..6754a3fbe 100644 --- a/images/Makefile.imx +++ b/images/Makefile.imx @@ -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 From d9485365b0065f367268573e1045c33d86dfac99 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 09:10:49 +0100 Subject: [PATCH 05/29] ARM: dts: i.MX53: Fix IPU register size Signed-off-by: Sascha Hauer --- arch/arm/dts/imx53.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/dts/imx53.dtsi b/arch/arm/dts/imx53.dtsi index 91659b8ca..c3fac4348 100644 --- a/arch/arm/dts/imx53.dtsi +++ b/arch/arm/dts/imx53.dtsi @@ -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"; From c9b67dc0f3b3d1127674c30cfbae52b4166d71d6 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 09:15:33 +0100 Subject: [PATCH 06/29] i2c: i.MX: move to earlier initcall Signed-off-by: Sascha Hauer --- drivers/i2c/busses/i2c-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index f657c287f..6b390478f 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -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); From 57eb30b265e1879b0cf3b0507066fb3b67a4195b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 09:15:56 +0100 Subject: [PATCH 07/29] i2c: implement of_find_i2c_adapter_by_node Signed-off-by: Sascha Hauer --- drivers/i2c/i2c.c | 11 +++++++++++ include/i2c/i2c.h | 1 + 2 files changed, 12 insertions(+) diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c index e289a3569..987395701 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -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) diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h index 81e5daa20..f89fefbb2 100644 --- a/include/i2c/i2c.h +++ b/include/i2c/i2c.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. From 3d937ce3125648181af6e20458c0cb12a99a73e5 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 09:14:38 +0100 Subject: [PATCH 08/29] clk: implement clk_round_rate Instead of returning just the current rate implement clk_round_rate properly. Signed-off-by: Sascha Hauer --- drivers/clk/clk.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 31d73c028..c67b2f4e4 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -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); } From b33e5ba246860cc3fa6b79ed82d51c47f936f651 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 09:19:36 +0100 Subject: [PATCH 09/29] clk: clk-mux: pass clk flags from initializers struct clk has a flags field, let the clk-mux initializers set this field. Signed-off-by: Sascha Hauer --- arch/arm/mach-clps711x/clock.c | 4 ++-- arch/arm/mach-imx/clk.h | 2 +- drivers/clk/clk-mux.c | 8 +++++--- drivers/clk/mxs/clk.h | 2 +- drivers/clk/tegra/clk-periph.c | 2 +- include/linux/clk.h | 6 ++++-- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-clps711x/clock.c b/arch/arm/mach-clps711x/clock.c index 7658c9aa7..548b333b1 100644 --- a/arch/arm/mach-clps711x/clock.c +++ b/arch/arm/mach-clps711x/clock.c @@ -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); diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 4a7298d84..8e218fb57 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -16,7 +16,7 @@ static inline struct clk *imx_clk_fixed_factor(const char *name, 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_gate(const char *name, const char *parent, diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index b22bdd1ec..9dcd39c2b 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -56,7 +56,8 @@ static struct clk_ops clk_mux_ops = { }; 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 +66,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 +81,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) { diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h index b4fcfa009..3db38b491 100644 --- a/drivers/clk/mxs/clk.h +++ b/drivers/clk/mxs/clk.h @@ -40,7 +40,7 @@ static inline struct clk *mxs_clk_gate(const char *name, 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, diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index e96949587..0b9c8dce1 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -140,7 +140,7 @@ 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; diff --git a/include/linux/clk.h b/include/linux/clk.h index af38c720e..439e88cf5 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -252,10 +252,12 @@ struct clk *clk_fixed_factor(const char *name, const char *parent, unsigned int mult, unsigned int div); 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); From f4c753651406821cb65799b315df86ef22a5c05f Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 09:37:12 +0100 Subject: [PATCH 10/29] clk: clk-gate: pass flags to initializers Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/clk.h | 2 +- arch/arm/mach-zynq/clk-zynq7000.c | 8 ++++---- drivers/clk/clk-gate.c | 11 ++++++----- drivers/clk/mvebu/common.c | 2 +- drivers/clk/mxs/clk-imx28.c | 2 +- drivers/clk/mxs/clk.h | 2 +- drivers/clk/tegra/clk-periph.c | 2 +- include/linux/clk.h | 6 +++--- 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 8e218fb57..072af55c4 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -22,7 +22,7 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, 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, 0); } struct clk *imx_clk_pllv1(const char *name, const char *parent, diff --git a/arch/arm/mach-zynq/clk-zynq7000.c b/arch/arm/mach-zynq/clk-zynq7000.c index 6472c642f..ea637d763 100644 --- a/arch/arm/mach-zynq/clk-zynq7000.c +++ b/arch/arm/mach-zynq/clk-zynq7000.c @@ -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); diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index baec85598..f356de763 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -83,7 +83,7 @@ static struct clk_ops clk_gate_ops = { }; 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 +92,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 +107,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 +124,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; diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index 37cc1568a..aa1e8f697 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -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)); } diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 05b3c2a04..934a1940f 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -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]); diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h index 3db38b491..168fa5823 100644 --- a/drivers/clk/mxs/clk.h +++ b/drivers/clk/mxs/clk.h @@ -34,7 +34,7 @@ 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, diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 0b9c8dce1..c970f63af 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -145,7 +145,7 @@ struct clk *_tegra_clk_register_periph(const char *name, 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; diff --git a/include/linux/clk.h b/include/linux/clk.h index 439e88cf5..21edfabd3 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -260,12 +260,12 @@ struct clk *clk_mux(const char *name, void __iomem *reg, 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); From 82163afcf0feb35f88a08362b27ed3e18c5072d1 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 09:41:28 +0100 Subject: [PATCH 11/29] clk: clk-fixed-factor: pass flags to initializers Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/clk.h | 2 +- drivers/clk/clk-fixed-factor.c | 3 ++- drivers/clk/mvebu/common.c | 2 +- drivers/clk/mxs/clk.h | 2 +- drivers/clk/tegra/clk-tegra20.c | 2 +- include/linux/clk.h | 3 ++- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 072af55c4..54f04ed55 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -10,7 +10,7 @@ static inline struct clk *imx_clk_divider(const char *name, const char *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, 0); } static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index f0ecbd3e5..df0d2f345 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -40,7 +40,7 @@ static struct clk_ops clk_fixed_factor_ops = { }; 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 +50,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; diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index aa1e8f697..658ce3e81 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -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])); }; diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h index 168fa5823..7bab7b5e6 100644 --- a/drivers/clk/mxs/clk.h +++ b/drivers/clk/mxs/clk.h @@ -46,7 +46,7 @@ static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg, 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 */ diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index f68c811a8..cfb719f43 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -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 */ diff --git a/include/linux/clk.h b/include/linux/clk.h index 21edfabd3..e65a62aa9 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -249,7 +249,8 @@ 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 *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, From 3e9a71f78de4107d6b991fe6e83bdc7749fd692b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 10:02:23 +0100 Subject: [PATCH 12/29] clk: clk-divider: pass flags to initializers Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/clk-imx6.c | 2 +- arch/arm/mach-imx/clk.h | 2 +- drivers/clk/clk-divider-table.c | 3 ++- drivers/clk/clk-divider.c | 7 ++++--- include/linux/clk.h | 6 +++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-imx/clk-imx6.c b/arch/arm/mach-imx/clk-imx6.c index 5560fff7c..8d78a1832 100644 --- a/arch/arm/mach-imx/clk-imx6.c +++ b/arch/arm/mach-imx/clk-imx6.c @@ -214,7 +214,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] = clk_divider_table("enet_ref", "pll6_enet", base + 0xe0, 0, 2, clk_enet_ref_table, 0); /* name parent_name reg idx */ clks[pll2_pfd0_352m] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 54f04ed55..cdafad1e9 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -4,7 +4,7 @@ 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, 0); } static inline struct clk *imx_clk_fixed_factor(const char *name, diff --git a/drivers/clk/clk-divider-table.c b/drivers/clk/clk-divider-table.c index fd2d3fc3c..0caf85d72 100644 --- a/drivers/clk/clk-divider-table.c +++ b/drivers/clk/clk-divider-table.c @@ -85,7 +85,7 @@ static struct clk_ops clk_divider_table_ops = { 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_divider_table *div = xzalloc(sizeof(*div)); const struct clk_div_table *clkt; @@ -98,6 +98,7 @@ struct clk *clk_divider_table(const char *name, div->clk.ops = &clk_divider_table_ops; div->clk.name = name; div->clk.parent_names = &div->parent; + div->clk.flags = flags; div->clk.num_parents = 1; div->table = table; diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 3bf8105a8..bb8bcc126 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -78,7 +78,7 @@ 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_divider *div = xzalloc(sizeof(*div)); int ret; @@ -89,6 +89,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 +103,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; diff --git a/include/linux/clk.h b/include/linux/clk.h index e65a62aa9..7e5010ade 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -242,12 +242,12 @@ struct clk_divider { 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, unsigned flags); From 503cc1518e2b94ae16a001b8f8a5adbd4ffd8829 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 10:03:51 +0100 Subject: [PATCH 13/29] clk: introduce CLK_SET_RATE_PARENT flag Signed-off-by: Sascha Hauer --- include/linux/clk.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/clk.h b/include/linux/clk.h index 7e5010ade..2704b0f05 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -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); From d4aaca3647fe900a4e6fe87c1d9717c3e5292386 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 10:04:46 +0100 Subject: [PATCH 14/29] clk: clk-divider: sync with kernel code This updates the clk-divider to Kernel code, but without power-of-two divider support which we do not need yet. This also adds table based divider support to the divider. Signed-off-by: Sascha Hauer --- drivers/clk/clk-divider.c | 201 +++++++++++++++++++++++++++++++------- include/linux/clk.h | 3 + 2 files changed, 169 insertions(+), 35 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index bb8bcc126..9634bd3c8 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -20,61 +20,192 @@ #include #include -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, diff --git a/include/linux/clk.h b/include/linux/clk.h index 2704b0f05..71b046607 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -240,6 +240,9 @@ 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; From d043e162bbc78d254a7d2295f6f75116399f3fab Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 10:07:44 +0100 Subject: [PATCH 15/29] clk: let clk-divider handle the table based divider aswell Signed-off-by: Sascha Hauer --- drivers/clk/Makefile | 2 +- drivers/clk/clk-divider-table.c | 120 -------------------------------- drivers/clk/clk-divider.c | 36 ++++++++++ 3 files changed, 37 insertions(+), 121 deletions(-) delete mode 100644 drivers/clk/clk-divider-table.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f5c05920b..7506f876a 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -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/ diff --git a/drivers/clk/clk-divider-table.c b/drivers/clk/clk-divider-table.c deleted file mode 100644 index 0caf85d72..000000000 --- a/drivers/clk/clk-divider-table.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * clk-divider-table.c - generic barebox clock support. Based on Linux clk support - * - * 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. - * - */ -#include -#include -#include -#include -#include - -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, unsigned flags) -{ - 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.flags = flags; - 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; -} diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 9634bd3c8..67783daab 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -248,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; +} From b1cc0d7fc6728acbed7360b315593c3535ce7d75 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 10:15:30 +0100 Subject: [PATCH 16/29] clk: clk-fixed-factor: add set_rate/round_rate callbacks Signed-off-by: Sascha Hauer --- drivers/clk/clk-fixed-factor.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index df0d2f345..cb531b146 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -35,7 +35,37 @@ 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, }; From 1184234a5ed338f745ec36e425f166cf9d16ae5b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 10:19:15 +0100 Subject: [PATCH 17/29] clk: Add parent round/set rate for mux and gate Signed-off-by: Sascha Hauer --- drivers/clk/clk-gate.c | 2 ++ drivers/clk/clk-mux.c | 2 ++ drivers/clk/clk.c | 20 ++++++++++++++++++++ include/linux/clk.h | 4 ++++ 4 files changed, 28 insertions(+) diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index f356de763..b298b193d 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -77,6 +77,8 @@ 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, diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 9dcd39c2b..4ce86f43d 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -51,6 +51,8 @@ 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, }; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c67b2f4e4..0d259413a 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -259,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 diff --git a/include/linux/clk.h b/include/linux/clk.h index 71b046607..07b27cf14 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -276,6 +276,10 @@ struct clk *clk_gate_inverted(const char *name, const char *parent, void __iomem 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); From 6d610de61c17c4caeeb2815dedf840b06efa61ad Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 10:13:59 +0100 Subject: [PATCH 18/29] ARM: i.MX: introduce clk parent rate changes Let dividers and gates change the parent rates. Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/clk-imx6.c | 2 +- arch/arm/mach-imx/clk.h | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-imx/clk-imx6.c b/arch/arm/mach-imx/clk-imx6.c index 8d78a1832..db16bb5e4 100644 --- a/arch/arm/mach-imx/clk-imx6.c +++ b/arch/arm/mach-imx/clk-imx6.c @@ -214,7 +214,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, 0); + 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); diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index cdafad1e9..d83266b77 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -4,13 +4,21 @@ 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, 0); + 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, 0); + 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, @@ -22,7 +30,7 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, 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, 0); + return clk_gate(name, parent, reg, shift, CLK_SET_RATE_PARENT); } struct clk *imx_clk_pllv1(const char *name, const char *parent, From ce6755ca1c41b8dd3b40cf3c9d6f3a1237a92720 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 10:34:00 +0100 Subject: [PATCH 19/29] ARM: i.MX6: Add video clocks This adds the IPU, LVDS and HDMI clocks. As these are many, depend on the IPU driver being compiled in. Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/clk-imx6.c | 153 ++++++++++++++++++++- arch/arm/mach-imx/clk.h | 6 + arch/arm/mach-imx/include/mach/imx6-regs.h | 3 + 3 files changed, 158 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-imx/clk-imx6.c b/arch/arm/mach-imx/clk-imx6.c index db16bb5e4..518fc00d9 100644 --- a/arch/arm/mach-imx/clk-imx6.c +++ b/arch/arm/mach-imx/clk-imx6.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #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; @@ -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 */ diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index d83266b77..32a02db17 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -27,6 +27,12 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, 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) { diff --git a/arch/arm/mach-imx/include/mach/imx6-regs.h b/arch/arm/mach-imx/include/mach/imx6-regs.h index 833280af5..facbe51b7 100644 --- a/arch/arm/mach-imx/include/mach/imx6-regs.h +++ b/arch/arm/mach-imx/include/mach/imx6-regs.h @@ -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) From 297b0e4672a7273fcb19acea1c5681db9f87ae37 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 14:32:26 +0100 Subject: [PATCH 20/29] video: introduce struct display_timings And use it inside struct fb_info. This struct has the advantage that the supported modes can be passed around in a single pointer. Signed-off-by: Sascha Hauer --- drivers/video/atmel_lcdfb_core.c | 6 +++--- drivers/video/fb.c | 20 +++++++++++--------- drivers/video/imx-ipu-fb.c | 4 ++-- drivers/video/imx.c | 6 +++--- drivers/video/omap.c | 4 ++-- drivers/video/s3c24xx.c | 6 +++--- drivers/video/sdl.c | 4 ++-- drivers/video/stm.c | 6 +++--- include/fb.h | 16 ++++++++++++++-- 9 files changed, 43 insertions(+), 29 deletions(-) diff --git a/drivers/video/atmel_lcdfb_core.c b/drivers/video/atmel_lcdfb_core.c index bed540da9..d03922b2b 100644 --- a/drivers/video/atmel_lcdfb_core.c +++ b/drivers/video/atmel_lcdfb_core.c @@ -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; diff --git a/drivers/video/fb.c b/drivers/video/fb.c index c36b9adce..e614d77c8 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -53,6 +53,7 @@ static int fb_setup_mode(struct device_d *dev, struct param_d *param, const char *val) { struct fb_info *info = dev->priv; + struct display_timings *dt; int mode, ret; if (info->enabled != 0) @@ -61,14 +62,15 @@ static int fb_setup_mode(struct device_d *dev, struct param_d *param, 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)) + dt = &info->modes; + for (mode = 0; mode < dt->num_modes; mode++) { + if (!strcmp(dt->modes[mode].name, val)) break; } - if (mode >= info->num_modes) + if (mode >= dt->num_modes) return -EINVAL; - info->mode = &info->mode_list[mode]; + info->mode = &dt->modes[mode]; info->xres = info->mode->xres; info->yres = info->mode->yres; @@ -105,13 +107,13 @@ static void fb_info(struct device_d *dev) struct fb_info *info = dev->priv; int i; - if (!info->num_modes) + if (!info->modes.num_modes) return; printf("available modes:\n"); - for (i = 0; i < info->num_modes; i++) { - struct fb_videomode *mode = &info->mode_list[i]; + for (i = 0; i < info->modes.num_modes; i++) { + struct fb_videomode *mode = &info->modes.modes[i]; printf("%-10s %dx%d@%d\n", mode->name, mode->xres, mode->yres, mode->refresh); @@ -155,10 +157,10 @@ 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) && + if (info->modes.num_modes && (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); + dev_set_param(dev, "mode_name", info->modes.modes[0].name); } ret = devfs_create(&info->cdev); diff --git a/drivers/video/imx-ipu-fb.c b/drivers/video/imx-ipu-fb.c index 727664738..0ee2b26dc 100644 --- a/drivers/video/imx-ipu-fb.c +++ b/drivers/video/imx-ipu-fb.c @@ -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); diff --git a/drivers/video/imx.c b/drivers/video/imx.c index 05c6105c7..1dd6511d0 100644 --- a/drivers/video/imx.c +++ b/drivers/video/imx.c @@ -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; diff --git a/drivers/video/omap.c b/drivers/video/omap.c index 487aca6f2..485cc7538 100644 --- a/drivers/video/omap.c +++ b/drivers/video/omap.c @@ -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; diff --git a/drivers/video/s3c24xx.c b/drivers/video/s3c24xx.c index c3e05c6cd..b1883e232 100644 --- a/drivers/video/s3c24xx.c +++ b/drivers/video/s3c24xx.c @@ -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) diff --git a/drivers/video/sdl.c b/drivers/video/sdl.c index 8dec5e577..a56834004 100644 --- a/drivers/video/sdl.c +++ b/drivers/video/sdl.c @@ -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; diff --git a/drivers/video/stm.c b/drivers/video/stm.c index fdeaf9c64..175e4b611 100644 --- a/drivers/video/stm.c +++ b/drivers/video/stm.c @@ -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) diff --git a/include/fb.h b/include/fb.h index 22fa9b130..1cc352ba8 100644 --- a/include/fb.h +++ b/include/fb.h @@ -77,10 +77,22 @@ 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 fb_info { struct fb_videomode *mode; - struct fb_videomode *mode_list; - unsigned num_modes; + struct display_timings modes; struct fb_ops *fbops; struct device_d dev; /* This is this fb device */ From 25dcdd68fba532d60352feeae4d38e3ac8e35188 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 15:43:24 +0100 Subject: [PATCH 21/29] video: rework mode_name parameter setting We have dev_add_param_enum() now, so use it for the mode_name setting. Also drop the special case for single mode framebuffers, just add the mode_name parameter for this case aswell. Signed-off-by: Sascha Hauer --- drivers/video/fb.c | 107 +++++++++++++++++++++++++++++++-------------- include/fb.h | 2 + 2 files changed, 76 insertions(+), 33 deletions(-) diff --git a/drivers/video/fb.c b/drivers/video/fb.c index e614d77c8..d7a3d3cff 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -49,34 +49,42 @@ 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; - struct display_timings *dt; - int mode, ret; + int num_modes; + + num_modes = info->modes.num_modes; + + if (num >= num_modes) + return NULL; + + 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); - - dt = &info->modes; - for (mode = 0; mode < dt->num_modes; mode++) { - if (!strcmp(dt->modes[mode].name, val)) - break; - } - if (mode >= dt->num_modes) + mode = fb_num_to_mode(info, info->current_mode); + if (!mode) return -EINVAL; - info->mode = &dt->modes[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); @@ -87,13 +95,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, @@ -102,22 +121,27 @@ 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->modes.num_modes) - return; printf("available modes:\n"); - for (i = 0; i < info->modes.num_modes; i++) { - struct fb_videomode *mode = &info->modes.modes[i]; - - printf("%-10s %dx%d@%d\n", mode->name, - mode->xres, mode->yres, mode->refresh); - } + fb_print_modes(&info->modes); printf("\n"); } @@ -126,10 +150,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); @@ -157,11 +191,18 @@ int register_framebuffer(struct fb_info *info) dev_add_param_bool(dev, "enable", fb_enable_set, NULL, &info->p_enable, info); - if (info->modes.num_modes && - (info->fbops->fb_activate_var != NULL)) { - dev_add_param(dev, "mode_name", fb_setup_mode, NULL, 0); - dev_set_param(dev, "mode_name", info->modes.modes[0].name); - } + num_modes = info->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; + + 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) diff --git a/include/fb.h b/include/fb.h index 1cc352ba8..91d3fe415 100644 --- a/include/fb.h +++ b/include/fb.h @@ -94,6 +94,8 @@ struct fb_info { struct fb_videomode *mode; struct display_timings modes; + int current_mode; + struct fb_ops *fbops; struct device_d dev; /* This is this fb device */ From 386876588642e024101502051625044e4e6f4922 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 15:56:26 +0100 Subject: [PATCH 22/29] video: Add display timing from devicetree helper Signed-off-by: Sascha Hauer --- .../arm/boards/chumby_falconwing/falconwing.c | 1 - .../boards/eukrea_cpuimx35/eukrea_cpuimx35.c | 1 - arch/arm/boards/freescale-mx21-ads/imx21ads.c | 1 - arch/arm/boards/freescale-mx28-evk/mx28-evk.c | 1 - arch/arm/boards/freescale-mx35-3ds/3stack.c | 1 - .../boards/friendlyarm-mini2440/mini2440.c | 1 - arch/arm/boards/guf-cupid/board.c | 1 - arch/arm/boards/karo-tx28/tx28-stk5.c | 5 - arch/arm/boards/phytec-phycore-imx35/pcm043.c | 2 - drivers/video/Makefile | 1 + drivers/video/of_display_timing.c | 238 ++++++++++++++++++ include/fb.h | 15 +- 12 files changed, 253 insertions(+), 15 deletions(-) create mode 100644 drivers/video/of_display_timing.c diff --git a/arch/arm/boards/chumby_falconwing/falconwing.c b/arch/arm/boards/chumby_falconwing/falconwing.c index 2ae902fb6..24dc6e321 100644 --- a/arch/arm/boards/chumby_falconwing/falconwing.c +++ b/arch/arm/boards/chumby_falconwing/falconwing.c @@ -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 diff --git a/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c index 3c8731160..912e13c32 100644 --- a/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c +++ b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c @@ -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) diff --git a/arch/arm/boards/freescale-mx21-ads/imx21ads.c b/arch/arm/boards/freescale-mx21-ads/imx21ads.c index 1bbd8cbf2..bd7e46e16 100644 --- a/arch/arm/boards/freescale-mx21-ads/imx21ads.c +++ b/arch/arm/boards/freescale-mx21-ads/imx21ads.c @@ -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, diff --git a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c index 3bbdb1d5c..dce6d3103 100644 --- a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c +++ b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c @@ -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, } }; diff --git a/arch/arm/boards/freescale-mx35-3ds/3stack.c b/arch/arm/boards/freescale-mx35-3ds/3stack.c index 2be1554b6..8f821adb7 100644 --- a/arch/arm/boards/freescale-mx35-3ds/3stack.c +++ b/arch/arm/boards/freescale-mx35-3ds/3stack.c @@ -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 = { diff --git a/arch/arm/boards/friendlyarm-mini2440/mini2440.c b/arch/arm/boards/friendlyarm-mini2440/mini2440.c index 2c2426016..86e22ad13 100644 --- a/arch/arm/boards/friendlyarm-mini2440/mini2440.c +++ b/arch/arm/boards/friendlyarm-mini2440/mini2440.c @@ -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 diff --git a/arch/arm/boards/guf-cupid/board.c b/arch/arm/boards/guf-cupid/board.c index e171331a4..127edaa17 100644 --- a/arch/arm/boards/guf-cupid/board.c +++ b/arch/arm/boards/guf-cupid/board.c @@ -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) diff --git a/arch/arm/boards/karo-tx28/tx28-stk5.c b/arch/arm/boards/karo-tx28/tx28-stk5.c index 57cb0c390..2aaceb442 100644 --- a/arch/arm/boards/karo-tx28/tx28-stk5.c +++ b/arch/arm/boards/karo-tx28/tx28-stk5.c @@ -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, }, }; diff --git a/arch/arm/boards/phytec-phycore-imx35/pcm043.c b/arch/arm/boards/phytec-phycore-imx35/pcm043.c index 46821a784..c1928cc8f 100644 --- a/arch/arm/boards/phytec-phycore-imx35/pcm043.c +++ b/arch/arm/boards/phytec-phycore-imx35/pcm043.c @@ -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, } }; diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 31edfca2b..d36d83d38 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_VIDEO) += fb.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 diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c new file mode 100644 index 000000000..5dfd5b3f1 --- /dev/null +++ b/drivers/video/of_display_timing.c @@ -0,0 +1,238 @@ +/* + * OF helpers for parsing display timings + * + * Copyright (c) 2012 Steffen Trumtrar , Pengutronix + * + * based on of_videomode.c by Sascha Hauer + * + * This file is released under the GPLv2 + */ +#include +#include +#include +#include + +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); diff --git a/include/fb.h b/include/fb.h index 91d3fe415..28e32fccd 100644 --- a/include/fb.h +++ b/include/fb.h @@ -4,6 +4,7 @@ #include #include #include +#include #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, @@ -125,6 +136,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) From 939c65332899e8b0c53fbeccc3cfd8d423e7211f Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Mar 2014 15:57:05 +0100 Subject: [PATCH 23/29] video: Add edid support Signed-off-by: Sascha Hauer --- drivers/video/Kconfig | 6 + drivers/video/Makefile | 1 + drivers/video/edid.c | 909 +++++++++++++++++++++++++++++++++++++++++ drivers/video/edid.h | 138 +++++++ drivers/video/fb.c | 14 +- include/fb.h | 11 +- 6 files changed, 1075 insertions(+), 4 deletions(-) create mode 100644 drivers/video/edid.c create mode 100644 drivers/video/edid.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 553926622..2bab95710 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -84,4 +84,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 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index d36d83d38..a33284875 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,4 +1,5 @@ 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 diff --git a/drivers/video/edid.c b/drivers/video/edid.c new file mode 100644 index 000000000..828c3610a --- /dev/null +++ b/drivers/video/edid.c @@ -0,0 +1,909 @@ +/* + * drivers/video/edid.c + * + * Copyright (C) 2002 James Simmons + * + * 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 + * + * 3. John Fremlin and + * Ani Joshi + * + * 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 +#include +#include +#include + +#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); +} diff --git a/drivers/video/edid.h b/drivers/video/edid.h new file mode 100644 index 000000000..006d9f283 --- /dev/null +++ b/drivers/video/edid.h @@ -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 + * + * 2. John Fremlin and + * Ani Joshi + * + * 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__ */ diff --git a/drivers/video/fb.c b/drivers/video/fb.c index d7a3d3cff..2c8b8eb25 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -53,11 +53,14 @@ static struct fb_videomode *fb_num_to_mode(struct fb_info *info, int num) { int num_modes; - num_modes = info->modes.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]; } @@ -142,6 +145,7 @@ static void fb_info(struct device_d *dev) printf("available modes:\n"); fb_print_modes(&info->modes); + fb_print_modes(&info->edid_modes); printf("\n"); } @@ -191,13 +195,17 @@ int register_framebuffer(struct fb_info *info) dev_add_param_bool(dev, "enable", fb_enable_set, NULL, &info->p_enable, info); - num_modes = info->modes.num_modes; + 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); diff --git a/include/fb.h b/include/fb.h index 28e32fccd..2db6ad6f3 100644 --- a/include/fb.h +++ b/include/fb.h @@ -101,12 +101,18 @@ struct display_timings { struct fb_videomode *modes; }; +struct i2c_adapter; + struct fb_info { struct fb_videomode *mode; 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 */ @@ -150,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 */ From 09821fba06e4c38069c075d8a36fd932eb3bd073 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 14 Mar 2014 09:43:09 +0100 Subject: [PATCH 24/29] ARM i.MX6q: Mark VPU and IPU AXI transfers as cacheable, increase IPU priority This is needed so that the IPU framebuffer scanout cannot be starved by VPU or GPU activity. Some boards like the SabreLite and SabreSD seem to set this in the DCD already, but the documented register reset values do not contain the necessary settings. Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/imx6.c | 19 +++++++++++++++++++ include/mfd/imx6q-iomuxc-gpr.h | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/arch/arm/mach-imx/imx6.c b/arch/arm/mach-imx/imx6.c index 304b1c0f2..e14ce90b4 100644 --- a/arch/arm/mach-imx/imx6.c +++ b/arch/arm/mach-imx/imx6.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -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) diff --git a/include/mfd/imx6q-iomuxc-gpr.h b/include/mfd/imx6q-iomuxc-gpr.h index 3ccc25401..24993ffe7 100644 --- a/include/mfd/imx6q-iomuxc-gpr.h +++ b/include/mfd/imx6q-iomuxc-gpr.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) From 0a4dece7800a14eca3ad5c89d482c770d9751c56 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 14 Mar 2014 10:19:46 +0100 Subject: [PATCH 25/29] video: Add kernel fourcc defines Signed-off-by: Sascha Hauer --- include/video/fourcc.h | 261 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 include/video/fourcc.h diff --git a/include/video/fourcc.h b/include/video/fourcc.h new file mode 100644 index 000000000..322142c1b --- /dev/null +++ b/include/video/fourcc.h @@ -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 */ From e5af227899c5650c39dd115f6e17db18f191e508 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 14 Mar 2014 10:14:58 +0100 Subject: [PATCH 26/29] video: Add i.MX IPUv3 support Signed-off-by: Sascha Hauer --- drivers/video/Kconfig | 2 + drivers/video/Makefile | 1 + drivers/video/imx-ipu-v3/Kconfig | 8 + drivers/video/imx-ipu-v3/Makefile | 3 + drivers/video/imx-ipu-v3/imx-ipu-v3.h | 344 ++++++++++ drivers/video/imx-ipu-v3/ipu-common.c | 836 +++++++++++++++++++++++++ drivers/video/imx-ipu-v3/ipu-dc.c | 392 ++++++++++++ drivers/video/imx-ipu-v3/ipu-di.c | 762 ++++++++++++++++++++++ drivers/video/imx-ipu-v3/ipu-dmfc.c | 397 ++++++++++++ drivers/video/imx-ipu-v3/ipu-dp.c | 313 +++++++++ drivers/video/imx-ipu-v3/ipu-prv.h | 204 ++++++ drivers/video/imx-ipu-v3/ipufb.c | 353 +++++++++++ drivers/video/imx-ipu-v3/ipuv3-plane.c | 239 +++++++ drivers/video/imx-ipu-v3/ipuv3-plane.h | 51 ++ 14 files changed, 3905 insertions(+) create mode 100644 drivers/video/imx-ipu-v3/Kconfig create mode 100644 drivers/video/imx-ipu-v3/Makefile create mode 100644 drivers/video/imx-ipu-v3/imx-ipu-v3.h create mode 100644 drivers/video/imx-ipu-v3/ipu-common.c create mode 100644 drivers/video/imx-ipu-v3/ipu-dc.c create mode 100644 drivers/video/imx-ipu-v3/ipu-di.c create mode 100644 drivers/video/imx-ipu-v3/ipu-dmfc.c create mode 100644 drivers/video/imx-ipu-v3/ipu-dp.c create mode 100644 drivers/video/imx-ipu-v3/ipu-prv.h create mode 100644 drivers/video/imx-ipu-v3/ipufb.c create mode 100644 drivers/video/imx-ipu-v3/ipuv3-plane.c create mode 100644 drivers/video/imx-ipu-v3/ipuv3-plane.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2bab95710..34177b3f0 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -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 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index a33284875..ae9f6e545 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -13,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/ diff --git a/drivers/video/imx-ipu-v3/Kconfig b/drivers/video/imx-ipu-v3/Kconfig new file mode 100644 index 000000000..84ef36367 --- /dev/null +++ b/drivers/video/imx-ipu-v3/Kconfig @@ -0,0 +1,8 @@ +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 + +endif diff --git a/drivers/video/imx-ipu-v3/Makefile b/drivers/video/imx-ipu-v3/Makefile new file mode 100644 index 000000000..0edc1a4a4 --- /dev/null +++ b/drivers/video/imx-ipu-v3/Makefile @@ -0,0 +1,3 @@ +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 diff --git a/drivers/video/imx-ipu-v3/imx-ipu-v3.h b/drivers/video/imx-ipu-v3/imx-ipu-v3.h new file mode 100644 index 000000000..7c48a7ce3 --- /dev/null +++ b/drivers/video/imx-ipu-v3/imx-ipu-v3.h @@ -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 +#include +#include