[U-Boot] [PATCH 00/37] rockchip: Add support for display and keyboard

This series adds display drivers for rockchip and enables them on jerry, firefly-rk3288 (HDMI only) and rock2 (HDMI only). It builds on the recent keyboard series.
Driver are provided for video displays (EDP and HDMI) and the VOP (video output processor).
This series also adds several new uclasses: - PWM (Pulse-width modulation) - Panel (used for LCD panels) - Display (renamed from DisplayPort - a generic display with power, etc.) - Backlight (used for LCD backlights)
Since most of this implementation is device-tree-controlled it is fairly easy to enable HDMI on the other RK3288 devices. This series adds this for Firefly-RK3288. It also includes Radxa Rock 2 as a separate board since a device tree file is now available in Linux. HDMI is enabled on that board also.
There remain quite a few things still to do for core rockchip support. Here are a few noticed while preparing this series:
- Clocks / caches are not working correctly. The 'dhry' command shows approximately 60 DMIPS when supposedly running at 1.8GHz. Turning the cache on or off makes no difference. The platform should run at over 2000 DMIPS by my estimate. This problem is common on ARM platforms. For example I found:
beaver (Tegra) 818 DMIPS pit (Exynos) 276 DMIPS snow (Exynos) 521 DMIPS
- HDMI EDID-reading does not work on Jerry, perhaps because the clocks are not running as they should. It seems to work fine on other boards.
This series is available at u-boot-dm/rkd-working
Simon Glass (37): stdio: Correct a build error with driver model gpio: Warn about invalid GPIOs used with the 'gpio' command video: Name consoles by their number video: Add a function to control cache flushing video: bridge: Allow GPIOs to be optional dm: pwm: Add a PWM uclass pwm: rockchip: Add a PWM driver for Rockchip SoCs dm: backlight: Add a backlight uclass dm: backlight: Add a driver for a PWM backlight dm: panel: Add a panel uclass video: panel: Add a simple panel driver dm: video: Repurpose the 'displayport' uclass to 'display' rockchip: Rename the CRU_MODE_CON fields rockchip: clk: Add support for clocks needed by the displays rockchip: video: Add a display driver for rockchip HDMI rockchip: video: Add a display driver for rockchip eDP rockchip: video: Add a video-output driver rockchip: Don't skip low-level init rockchip: Add a simple 'clock' command rockchip: Add a script to parse datasheets rockchip: config: Enable the 'gpio' command rockchip: sdram: Tidy up a few comments rockchip: sdram: Use syscon_get_first_range() where possible rockchip: Tidy up the register-access macros rockchip: spl: Drop MMC support code when not needed rockchip: jerry: Fix the SDRAM timing rockchip: rk3288: clock: Fix various minor errors rockchip: rk3288: pinctrl: Fix HDMI pinctrl rockchip: spl: Support full-speed CPU in SPL rockchip: jerry: Add support for timing SPI flash speed rockchip: jerry: Enable EDP and HDMI video output rockchip: firefly-rk3288: Enable HDMI output rockchip: dts: Sync up SPDIF node with Linux rockchip: rock2: Bring in device tree files from Linux rockchip: rock2: dts: Make changes for U-Boot rockchip: Add support for Raxda Rock 2 rockchip: Update the README
arch/arm/dts/Makefile | 1 + arch/arm/dts/rk3288-jerry.dts | 5 + arch/arm/dts/rk3288-rock2-som.dtsi | 278 ++++++ arch/arm/dts/rk3288-rock2-square.dts | 201 ++++ arch/arm/dts/rk3288-veyron.dtsi | 4 +- arch/arm/dts/rk3288.dtsi | 22 + arch/arm/include/asm/arch-rockchip/clock.h | 5 + arch/arm/include/asm/arch-rockchip/cru_rk3288.h | 70 +- arch/arm/include/asm/arch-rockchip/edp_rk3288.h | 636 +++++++++++++ arch/arm/include/asm/arch-rockchip/hardware.h | 7 +- arch/arm/include/asm/arch-rockchip/hdmi_rk3288.h | 456 +++++++++ arch/arm/include/asm/arch-rockchip/pwm.h | 41 + arch/arm/include/asm/arch-rockchip/vop_rk3288.h | 349 +++++++ arch/arm/mach-rockchip/board.c | 28 + arch/arm/mach-rockchip/rk3288-board-spl.c | 10 +- arch/arm/mach-rockchip/rk3288/Kconfig | 19 + arch/arm/mach-rockchip/rk3288/sdram_rk3288.c | 57 +- board/radxa/rock2/Kconfig | 15 + board/radxa/rock2/MAINTAINERS | 6 + board/radxa/rock2/Makefile | 7 + board/radxa/rock2/rock2.c | 7 + common/cmd_gpio.c | 4 +- common/stdio.c | 3 + configs/chromebook_jerry_defconfig | 7 + configs/firefly-rk3288_defconfig | 7 + configs/nyan-big_defconfig | 2 +- configs/rock2_defconfig | 53 ++ doc/README.rockchip | 53 +- drivers/Kconfig | 2 + drivers/clk/clk_rk3288.c | 290 +++++- drivers/pinctrl/rockchip/pinctrl_rk3288.c | 2 + drivers/pwm/Kconfig | 19 + drivers/pwm/Makefile | 2 + drivers/pwm/pwm-uclass.c | 36 + drivers/pwm/rk_pwm.c | 103 +++ drivers/video/Kconfig | 23 +- drivers/video/Makefile | 8 +- drivers/video/backlight-uclass.c | 25 + drivers/video/bridge/video-bridge-uclass.c | 11 +- drivers/video/display-uclass.c | 52 ++ drivers/video/dp-uclass.c | 34 - drivers/video/panel-uclass.c | 25 + drivers/video/pwm_backlight.c | 134 +++ drivers/video/rockchip/Makefile | 8 + drivers/video/rockchip/rk_edp.c | 1081 ++++++++++++++++++++++ drivers/video/rockchip/rk_hdmi.c | 929 +++++++++++++++++++ drivers/video/rockchip/rk_vop.c | 346 +++++++ drivers/video/simple_panel.c | 99 ++ drivers/video/tegra124/display.c | 18 +- drivers/video/tegra124/dp.c | 9 +- drivers/video/vidconsole-uclass.c | 7 +- drivers/video/video-uclass.c | 7 + include/backlight.h | 31 + include/configs/chromebook_jerry.h | 14 +- include/configs/firefly-rk3288.h | 9 +- include/configs/rk3288_common.h | 2 +- include/configs/rock2.h | 31 + include/configs/sandbox.h | 8 +- include/{displayport.h => display.h} | 33 +- include/dm/uclass-id.h | 5 +- include/edid.h | 1 + include/panel.h | 31 + include/pwm.h | 53 ++ include/video.h | 8 + tools/rkmux.py | 218 +++++ 65 files changed, 5864 insertions(+), 203 deletions(-) create mode 100644 arch/arm/dts/rk3288-rock2-som.dtsi create mode 100644 arch/arm/dts/rk3288-rock2-square.dts create mode 100644 arch/arm/include/asm/arch-rockchip/edp_rk3288.h create mode 100644 arch/arm/include/asm/arch-rockchip/hdmi_rk3288.h create mode 100644 arch/arm/include/asm/arch-rockchip/pwm.h create mode 100644 arch/arm/include/asm/arch-rockchip/vop_rk3288.h create mode 100644 board/radxa/rock2/Kconfig create mode 100644 board/radxa/rock2/MAINTAINERS create mode 100644 board/radxa/rock2/Makefile create mode 100644 board/radxa/rock2/rock2.c create mode 100644 configs/rock2_defconfig create mode 100644 drivers/pwm/Kconfig create mode 100644 drivers/pwm/pwm-uclass.c create mode 100644 drivers/pwm/rk_pwm.c create mode 100644 drivers/video/backlight-uclass.c create mode 100644 drivers/video/display-uclass.c delete mode 100644 drivers/video/dp-uclass.c create mode 100644 drivers/video/panel-uclass.c create mode 100644 drivers/video/pwm_backlight.c create mode 100644 drivers/video/rockchip/Makefile create mode 100644 drivers/video/rockchip/rk_edp.c create mode 100644 drivers/video/rockchip/rk_hdmi.c create mode 100644 drivers/video/rockchip/rk_vop.c create mode 100644 drivers/video/simple_panel.c create mode 100644 include/backlight.h create mode 100644 include/configs/rock2.h rename include/{displayport.h => display.h} (59%) create mode 100644 include/panel.h create mode 100755 tools/rkmux.py

When driver model is used for video but not for the keyboard, a compiler warnings is produced. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org ---
common/stdio.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/common/stdio.c b/common/stdio.c index 7252bab..f99cfe7 100644 --- a/common/stdio.c +++ b/common/stdio.c @@ -283,6 +283,9 @@ int stdio_add_devices(void) #endif #ifdef CONFIG_DM_VIDEO struct udevice *vdev; +# ifndef CONFIG_DM_KEYBOARD + int ret; +# endif
for (ret = uclass_first_device(UCLASS_VIDEO, &vdev); vdev;

At present there is no indication that an invalid GPIO is used except that the GPIO status is not displayed. Make the error more explicit to avoid confusion.
Signed-off-by: Simon Glass sjg@chromium.org ---
common/cmd_gpio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/common/cmd_gpio.c b/common/cmd_gpio.c index bb0f63a..2b78b16 100644 --- a/common/cmd_gpio.c +++ b/common/cmd_gpio.c @@ -174,8 +174,10 @@ static int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) * GPIO compatibility layer. */ ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio); - if (ret) + if (ret) { + printf("GPIO: '%s' not found\n", str_gpio); return cmd_process_error(cmdtp, ret); + } #else /* turn the gpio name into a gpio number */ gpio = name_to_gpio(str_gpio);

We must use the console name in the 'stdout' variable to select the one we want. At present the name is formed from the driver name with a suffix indicating the rotation value.
It seems better to name them sequentially since this can be controlled by driver order. So adjust the code to use 'vidconsole' for the first, 'vidconsole1' for the second, etc.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/video/vidconsole-uclass.c | 7 ++++++- include/configs/sandbox.h | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index ea10189..706a189 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -161,7 +161,12 @@ static int vidconsole_post_probe(struct udevice *dev) struct stdio_dev *sdev = &priv->sdev; int ret;
- strlcpy(sdev->name, dev->name, sizeof(sdev->name)); + if (dev->seq) { + snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d", + dev->seq); + } else { + strcpy(sdev->name, "vidconsole"); + } sdev->flags = DEV_FLAGS_OUTPUT; sdev->putc = vidconsole_putc; sdev->puts = vidconsole_puts; diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 6498981..4bffd8d 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -169,12 +169,12 @@ #define CONFIG_KEYBOARD
#define SANDBOX_SERIAL_SETTINGS "stdin=serial,cros-ec-keyb,usbkbd\0" \ - "stdout=serial,lcd.vidconsole\0" \ - "stderr=serial,lcd.vidconsole\0" + "stdout=serial,vidconsole\0" \ + "stderr=serial,vidconsole\0" #else #define SANDBOX_SERIAL_SETTINGS "stdin=serial\0" \ - "stdout=serial,lcd.vidconsole\0" \ - "stderr=serial,lcd.vidconsole\0" + "stdout=serial,vidconsole\0" \ + "stderr=serial,vidconsole\0" #endif
#define SANDBOX_ETH_SETTINGS "ethaddr=00:00:11:22:33:44\0" \

Allow the cache-flushing function of a video device to be controlled.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/video/video-uclass.c | 7 +++++++ include/video.h | 8 ++++++++ 2 files changed, 15 insertions(+)
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index 63d0d9d..24d537e 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -42,6 +42,13 @@ */ DECLARE_GLOBAL_DATA_PTR;
+void video_set_flush_dcache(struct udevice *dev, bool flush) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + priv->flush_dcache = flush; +} + static ulong alloc_fb(struct udevice *dev, ulong *addrp) { struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); diff --git a/include/video.h b/include/video.h index 5fd426e..5b147ba 100644 --- a/include/video.h +++ b/include/video.h @@ -159,6 +159,14 @@ int video_get_xsize(struct udevice *dev); */ int video_get_ysize(struct udevice *dev);
+/** + * Set whether we need to flush the dcache when changing the image. This + * defaults to off. + * + * @param flush non-zero to flush cache after update, 0 to skip + */ +void video_set_flush_dcache(struct udevice *dev, bool flush); + #endif /* CONFIG_DM_VIDEO */
#ifndef CONFIG_DM_VIDEO

Some video bridges will not have GPIOs to control reset, etc. Allow these to be optional.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/video/bridge/video-bridge-uclass.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c index 6c5990f..07270ba 100644 --- a/drivers/video/bridge/video-bridge-uclass.c +++ b/drivers/video/bridge/video-bridge-uclass.c @@ -55,7 +55,8 @@ static int video_bridge_pre_probe(struct udevice *dev) &uc_priv->sleep, GPIOD_IS_OUT); if (ret) { debug("%s: Could not decode sleep-gpios (%d)\n", __func__, ret); - return ret; + if (ret != -ENOENT) + return ret; } /* * Drop this for now as we do not have driver model pinctrl support @@ -70,7 +71,8 @@ static int video_bridge_pre_probe(struct udevice *dev) GPIOD_IS_OUT); if (ret) { debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret); - return ret; + if (ret != -ENOENT) + return ret; } /* * Drop this for now as we do not have driver model pinctrl support @@ -83,9 +85,10 @@ static int video_bridge_pre_probe(struct udevice *dev) */ ret = gpio_request_by_name(dev, "hotplug-gpios", 0, &uc_priv->hotplug, GPIOD_IS_IN); - if (ret && ret != -ENOENT) { + if (ret) { debug("%s: Could not decode hotplug (%d)\n", __func__, ret); - return ret; + if (ret != -ENOENT) + return ret; }
return 0;

Add a uclass that supports Pulse Width Modulation (PWM) devices. It provides methods to enable/disable and configure the device.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/Kconfig | 2 ++ drivers/pwm/Kconfig | 10 +++++++++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-uclass.c | 36 ++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/pwm.h | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 103 insertions(+) create mode 100644 drivers/pwm/Kconfig create mode 100644 drivers/pwm/pwm-uclass.c
diff --git a/drivers/Kconfig b/drivers/Kconfig index c481e93..70993fd 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -46,6 +46,8 @@ source "drivers/pinctrl/Kconfig"
source "drivers/power/Kconfig"
+source "drivers/pwm/Kconfig" + source "drivers/ram/Kconfig"
source "drivers/remoteproc/Kconfig" diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig new file mode 100644 index 0000000..bd47159 --- /dev/null +++ b/drivers/pwm/Kconfig @@ -0,0 +1,10 @@ +config DM_PWM + bool "Enable support for pulse-width modulation devices (PWM)" + depends on DM + help + A pulse-width modulator emits a pulse of varying width and provides + control over the duty cycle (high and low time) of the signal. This + is often used to control a voltage level. The more time the PWM + spends in the 'high' state, the higher the voltage. The PWM's + frequency/period can be controlled along with the proportion of that + time that the signal is high. diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index c0c4883..d1b15e5 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -10,4 +10,5 @@
#ccflags-y += -DDEBUG
+obj-$(CONFIG_DM_PWM) += pwm-uclass.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o diff --git a/drivers/pwm/pwm-uclass.c b/drivers/pwm/pwm-uclass.c new file mode 100644 index 0000000..c2200af --- /dev/null +++ b/drivers/pwm/pwm-uclass.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <pwm.h> + +int pwm_set_config(struct udevice *dev, uint channel, uint period_ns, + uint duty_ns) +{ + struct pwm_ops *ops = pwm_get_ops(dev); + + if (!ops->set_config) + return -ENOSYS; + + return ops->set_config(dev, channel, period_ns, duty_ns); +} + +int pwm_set_enable(struct udevice *dev, uint channel, bool enable) +{ + struct pwm_ops *ops = pwm_get_ops(dev); + + if (!ops->set_enable) + return -ENOSYS; + + return ops->set_enable(dev, channel, enable); +} + +UCLASS_DRIVER(pwm) = { + .id = UCLASS_PWM, + .name = "pwm", +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index b5f43ae..8f0381d 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -51,6 +51,7 @@ enum uclass_id { UCLASS_PINCTRL, /* Pinctrl (pin muxing/configuration) device */ UCLASS_PINCONFIG, /* Pin configuration node device */ UCLASS_PMIC, /* PMIC I/O device */ + UCLASS_PWM, /* Pulse-width modulator */ UCLASS_PWRSEQ, /* Power sequence device */ UCLASS_REGULATOR, /* Regulator device */ UCLASS_RESET, /* Reset device */ diff --git a/include/pwm.h b/include/pwm.h index f24f220..851915e 100644 --- a/include/pwm.h +++ b/include/pwm.h @@ -1,6 +1,7 @@ /* * header file for pwm driver. * + * Copyright 2016 Google Inc. * Copyright (c) 2011 samsung electronics * Donghwa Lee dh09.lee@samsung.com * @@ -10,9 +11,61 @@ #ifndef _pwm_h_ #define _pwm_h_
+/* struct pwm_ops: Operations for the PWM uclass */ +struct pwm_ops { + /** + * set_config() - Set the PWM configuration + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @period_ns: PWM period in nanoseconds + * @duty_ns: PWM duty period in nanoseconds + * @return 0 if OK, -ve on error + */ + int (*set_config)(struct udevice *dev, uint channel, uint period_ns, + uint duty_ns); + + /** + * set_enable() - Enable or disable the PWM + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @enable: true to enable, false to disable + * @return 0 if OK, -ve on error + */ + int (*set_enable)(struct udevice *dev, uint channel, bool enable); +}; + +#define pwm_get_ops(dev) ((struct pwm_ops *)(dev)->driver->ops) + +/** + * pwm_set_config() - Set the PWM configuration + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @period_ns: PWM period in nanoseconds + * @duty_ns: PWM duty period in nanoseconds + * @return 0 if OK, -ve on error + */ +int pwm_set_config(struct udevice *dev, uint channel, uint period_ns, + uint duty_ns); + +/** + * pwm_set_enable() - Enable or disable the PWM + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @enable: true to enable, false to disable + * @return 0 if OK, -ve on error + */ +int pwm_set_enable(struct udevice *dev, uint channel, bool enable); + +/* Legacy interface */ +#ifndef CONFIG_DM_PWM int pwm_init (int pwm_id, int div, int invert); int pwm_config (int pwm_id, int duty_ns, int period_ns); int pwm_enable (int pwm_id); void pwm_disable (int pwm_id); +#endif
#endif /* _pwm_h_ */

Add a simple driver which implements the standard PWM uclass interface.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/include/asm/arch-rockchip/pwm.h | 41 ++++++++++++ drivers/pwm/Kconfig | 9 +++ drivers/pwm/Makefile | 1 + drivers/pwm/rk_pwm.c | 103 +++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 arch/arm/include/asm/arch-rockchip/pwm.h create mode 100644 drivers/pwm/rk_pwm.c
diff --git a/arch/arm/include/asm/arch-rockchip/pwm.h b/arch/arm/include/asm/arch-rockchip/pwm.h new file mode 100644 index 0000000..08ff945 --- /dev/null +++ b/arch/arm/include/asm/arch-rockchip/pwm.h @@ -0,0 +1,41 @@ +/* + * (C) Copyright 2016 Google, Inc + * (C) Copyright 2008-2014 Rockchip Electronics + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ASM_ARCH_PWM_H +#define _ASM_ARCH_PWM_H + +struct rk3288_pwm { + u32 cnt; + u32 period_hpr; + u32 duty_lpr; + u32 ctrl; +}; +check_member(rk3288_pwm, ctrl, 0xc); + +#define RK_PWM_DISABLE (0 << 0) +#define RK_PWM_ENABLE (1 << 0) + +#define PWM_ONE_SHOT (0 << 1) +#define PWM_CONTINUOUS (1 << 1) +#define RK_PWM_CAPTURE (1 << 2) + +#define PWM_DUTY_POSTIVE (1 << 3) +#define PWM_DUTY_NEGATIVE (0 << 3) + +#define PWM_INACTIVE_POSTIVE (1 << 4) +#define PWM_INACTIVE_NEGATIVE (0 << 4) + +#define PWM_OUTPUT_LEFT (0 << 5) +#define PWM_OUTPUT_CENTER (1 << 5) + +#define PWM_LP_ENABLE (1 << 8) +#define PWM_LP_DISABLE (0 << 8) + +#define PWM_SEL_SCALE_CLK (1 << 9) +#define PWM_SEL_SRC_CLK (0 << 9) + +#endif diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index bd47159..cd8f357 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -8,3 +8,12 @@ config DM_PWM spends in the 'high' state, the higher the voltage. The PWM's frequency/period can be controlled along with the proportion of that time that the signal is high. + +config PWM_ROCKCHIP + bool "Enable support for the Rockchip PWM" + depends on DM_PWM + help + This PWM is found on RK3288 and other Rockchip SoCs. It supports a + programmable period and duty cycle. A 32-bit counter is used. + Various options provided in the hardware (such as capture mode and + continuous/single-shot) are not supported by the driver. diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index d1b15e5..b6d8c16 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -11,4 +11,5 @@ #ccflags-y += -DDEBUG
obj-$(CONFIG_DM_PWM) += pwm-uclass.o +obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o diff --git a/drivers/pwm/rk_pwm.c b/drivers/pwm/rk_pwm.c new file mode 100644 index 0000000..2d289a4 --- /dev/null +++ b/drivers/pwm/rk_pwm.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <div64.h> +#include <dm.h> +#include <pwm.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/cru_rk3288.h> +#include <asm/arch/grf_rk3288.h> +#include <asm/arch/pwm.h> +#include <power/regulator.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct rk_pwm_priv { + struct rk3288_pwm *regs; + struct rk3288_grf *grf; +}; + +static int rk_pwm_set_config(struct udevice *dev, uint channel, uint period_ns, + uint duty_ns) +{ + struct rk_pwm_priv *priv = dev_get_priv(dev); + struct rk3288_pwm *regs = priv->regs; + unsigned long period, duty; + + debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns); + writel(PWM_SEL_SRC_CLK | PWM_OUTPUT_LEFT | PWM_LP_DISABLE | + PWM_CONTINUOUS | PWM_DUTY_POSTIVE | PWM_INACTIVE_POSTIVE | + RK_PWM_DISABLE, + ®s->ctrl); + + period = lldiv((uint64_t)(PD_BUS_PCLK_HZ / 1000) * period_ns, 1000000); + duty = lldiv((uint64_t)(PD_BUS_PCLK_HZ / 1000) * duty_ns, 1000000); + + writel(period, ®s->period_hpr); + writel(duty, ®s->duty_lpr); + debug("%s: period=%lu, duty=%lu\n", __func__, period, duty); + + return 0; +} + +static int rk_pwm_set_enable(struct udevice *dev, uint channel, bool enable) +{ + struct rk_pwm_priv *priv = dev_get_priv(dev); + struct rk3288_pwm *regs = priv->regs; + + debug("%s: Enable '%s'\n", __func__, dev->name); + clrsetbits_le32(®s->ctrl, RK_PWM_ENABLE, enable ? RK_PWM_ENABLE : 0); + + return 0; +} + +static int rk_pwm_ofdata_to_platdata(struct udevice *dev) +{ + struct rk_pwm_priv *priv = dev_get_priv(dev); + struct regmap *map; + + priv->regs = (struct rk3288_pwm *)dev_get_addr(dev); + map = syscon_get_regmap_by_driver_data(ROCKCHIP_SYSCON_GRF); + if (IS_ERR(map)) + return PTR_ERR(map); + priv->grf = regmap_get_range(map, 0); + + return 0; +} + +static int rk_pwm_probe(struct udevice *dev) +{ + struct rk_pwm_priv *priv = dev_get_priv(dev); + + rk_setreg(&priv->grf->soc_con2, 1 << 0); + + return 0; +} + +static const struct pwm_ops rk_pwm_ops = { + .set_config = rk_pwm_set_config, + .set_enable = rk_pwm_set_enable, +}; + +static const struct udevice_id rk_pwm_ids[] = { + { .compatible = "rockchip,rk3288-pwm" }, + { } +}; + +U_BOOT_DRIVER(rk_pwm) = { + .name = "rk_pwm", + .id = UCLASS_PWM, + .of_match = rk_pwm_ids, + .ops = &rk_pwm_ops, + .ofdata_to_platdata = rk_pwm_ofdata_to_platdata, + .probe = rk_pwm_probe, + .priv_auto_alloc_size = sizeof(struct rk_pwm_priv), +};

LCD panels normally have a backlight which can be controlled to illuminate the LCD contents. Add a uclass to support this. Initially it only has a method to enable the backlight.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/video/Makefile | 1 + drivers/video/backlight-uclass.c | 25 +++++++++++++++++++++++++ include/backlight.h | 31 +++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 4 files changed, 58 insertions(+) create mode 100644 drivers/video/backlight-uclass.c create mode 100644 include/backlight.h
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ee04629..fa90721 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -7,6 +7,7 @@
ifdef CONFIG_DM obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o +obj-$(CONFIG_DM_VIDEO) += backlight-uclass.o obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o obj-$(CONFIG_DM_VIDEO) += video_bmp.o obj-$(CONFIG_VIDEO_ROTATION) += console_rotate.o diff --git a/drivers/video/backlight-uclass.c b/drivers/video/backlight-uclass.c new file mode 100644 index 0000000..0238289 --- /dev/null +++ b/drivers/video/backlight-uclass.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <backlight.h> + +int backlight_enable(struct udevice *dev) +{ + const struct backlight_ops *ops = backlight_get_ops(dev); + + if (!ops->enable) + return -ENOSYS; + + return ops->enable(dev); +} + +UCLASS_DRIVER(backlight) = { + .id = UCLASS_PANEL_BACKLIGHT, + .name = "backlight", +}; diff --git a/include/backlight.h b/include/backlight.h new file mode 100644 index 0000000..90e1d98 --- /dev/null +++ b/include/backlight.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _BACKLIGHT_H +#define _BACKLIGHT_H + +struct backlight_ops { + /** + * enable() - Enable a backlight + * + * @dev: Backlight device to enable + * @return 0 if OK, -ve on error + */ + int (*enable)(struct udevice *dev); +}; + +#define backlight_get_ops(dev) ((struct backlight_ops *)(dev)->driver->ops) + +/** + * backlight_enable() - Enable a backlight + * + * @dev: Backlight device to enable + * @return 0 if OK, -ve on error + */ +int backlight_enable(struct udevice *dev); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 8f0381d..5421981 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -45,6 +45,7 @@ enum uclass_id { UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ UCLASS_MTD, /* Memory Technology Device (MTD) device */ + UCLASS_PANEL_BACKLIGHT, /* Backlight controller for panel */ UCLASS_PCH, /* x86 platform controller hub */ UCLASS_PCI, /* PCI bus */ UCLASS_PCI_GENERIC, /* Generic PCI bus device */

Many backlights need to use a PWM to control the brightness. Add a driver for this. It understands the standard device tree binding.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/video/Makefile | 3 + drivers/video/pwm_backlight.c | 134 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 drivers/video/pwm_backlight.c
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index fa90721..6d89532 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -10,6 +10,9 @@ obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o obj-$(CONFIG_DM_VIDEO) += backlight-uclass.o obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o obj-$(CONFIG_DM_VIDEO) += video_bmp.o +ifdef CONFIG_DM_VIDEO +obj-$(CONFIG_DM_PWM) += pwm_backlight.o +endif obj-$(CONFIG_VIDEO_ROTATION) += console_rotate.o endif
diff --git a/drivers/video/pwm_backlight.c b/drivers/video/pwm_backlight.c new file mode 100644 index 0000000..de6277f --- /dev/null +++ b/drivers/video/pwm_backlight.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <backlight.h> +#include <pwm.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct pwm_backlight_priv { + struct udevice *reg; + struct gpio_desc enable; + struct udevice *pwm; + uint channel; + uint period_ns; + uint default_level; + uint min_level; + uint max_level; +}; + +static int pwm_backlight_enable(struct udevice *dev) +{ + struct pwm_backlight_priv *priv = dev_get_priv(dev); + uint duty_cycle; + int ret; + + debug("%s: Enable '%s', regulator '%s'\n", __func__, dev->name, + priv->reg->name); + ret = regulator_set_enable(priv->reg, true); + if (ret) { + debug("%s: Cannot enable regulator for PWM '%s'\n", __func__, + dev->name); + return ret; + } + mdelay(120); + + duty_cycle = priv->period_ns * (priv->default_level - priv->min_level) / + (priv->max_level - priv->min_level + 1); + ret = pwm_set_config(priv->pwm, priv->channel, priv->period_ns, + duty_cycle); + if (ret) + return ret; + ret = pwm_set_enable(priv->pwm, priv->channel, true); + if (ret) + return ret; + mdelay(10); + dm_gpio_set_value(&priv->enable, 1); + + return 0; +} + +static int pwm_backlight_ofdata_to_platdata(struct udevice *dev) +{ + struct pwm_backlight_priv *priv = dev_get_priv(dev); + struct fdtdec_phandle_args args; + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + int index, ret, count, len; + const u32 *cell; + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "power-supply", &priv->reg); + if (ret) { + debug("%s: Cannot get power supply: ret=%d\n", __func__, ret); + return ret; + } + ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable, + GPIOD_IS_OUT); + if (ret) { + debug("%s: Warning: cannot get enable GPIO: ret=%d\n", + __func__, ret); + if (ret != -ENOENT) + return ret; + } + ret = fdtdec_parse_phandle_with_args(blob, node, "pwms", "#pwm-cells", + 0, 0, &args); + if (ret) { + debug("%s: Cannot get PWM phandle: ret=%d\n", __func__, ret); + return ret; + } + + ret = uclass_get_device_by_of_offset(UCLASS_PWM, args.node, &priv->pwm); + if (ret) { + debug("%s: Cannot get PWM: ret=%d\n", __func__, ret); + return ret; + } + priv->channel = args.args[0]; + priv->period_ns = args.args[1]; + + index = fdtdec_get_int(blob, node, "default-brightness-level", 255); + cell = fdt_getprop(blob, node, "brightness-levels", &len); + count = len / sizeof(u32); + if (cell && count > index) { + priv->default_level = fdt32_to_cpu(cell[index]); + priv->max_level = fdt32_to_cpu(cell[count - 1]); + } else { + priv->default_level = index; + priv->max_level = 255; + } + + + return 0; +} + +static int pwm_backlight_probe(struct udevice *dev) +{ + return 0; +} + +static const struct backlight_ops pwm_backlight_ops = { + .enable = pwm_backlight_enable, +}; + +static const struct udevice_id pwm_backlight_ids[] = { + { .compatible = "pwm-backlight" }, + { } +}; + +U_BOOT_DRIVER(pwm_backlight) = { + .name = "pwm_backlight", + .id = UCLASS_PANEL_BACKLIGHT, + .of_match = pwm_backlight_ids, + .ops = &pwm_backlight_ops, + .ofdata_to_platdata = pwm_backlight_ofdata_to_platdata, + .probe = pwm_backlight_probe, + .priv_auto_alloc_size = sizeof(struct pwm_backlight_priv), +};

LCD panels can usefully be modelled as their own uclass. They can be probed (which powers them up ready for use). If they have a backlight, this can be enabled.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/video/Makefile | 1 + drivers/video/panel-uclass.c | 25 +++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/panel.h | 31 +++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 drivers/video/panel-uclass.c create mode 100644 include/panel.h
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6d89532..d29280c 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -8,6 +8,7 @@ ifdef CONFIG_DM obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o obj-$(CONFIG_DM_VIDEO) += backlight-uclass.o +obj-$(CONFIG_DM_VIDEO) += panel-uclass.o obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o obj-$(CONFIG_DM_VIDEO) += video_bmp.o ifdef CONFIG_DM_VIDEO diff --git a/drivers/video/panel-uclass.c b/drivers/video/panel-uclass.c new file mode 100644 index 0000000..3f4c41b --- /dev/null +++ b/drivers/video/panel-uclass.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <panel.h> + +int panel_enable_backlight(struct udevice *dev) +{ + struct panel_ops *ops = panel_get_ops(dev); + + if (!ops->enable_backlight) + return -ENOSYS; + + return ops->enable_backlight(dev); +} + +UCLASS_DRIVER(panel) = { + .id = UCLASS_PANEL, + .name = "panel", +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 5421981..8308c23 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -45,6 +45,7 @@ enum uclass_id { UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ UCLASS_MTD, /* Memory Technology Device (MTD) device */ + UCLASS_PANEL, /* Display panel, such as an LCD */ UCLASS_PANEL_BACKLIGHT, /* Backlight controller for panel */ UCLASS_PCH, /* x86 platform controller hub */ UCLASS_PCI, /* PCI bus */ diff --git a/include/panel.h b/include/panel.h new file mode 100644 index 0000000..57fccf2 --- /dev/null +++ b/include/panel.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _PANEL_H +#define _PANEL_H + +struct panel_ops { + /** + * enable_backlight() - Enable the panel backlight + * + * @dev: Panel device containing the backlight to enable + * @return 0 if OK, -ve on error + */ + int (*enable_backlight)(struct udevice *dev); +}; + +#define panel_get_ops(dev) ((struct panel_ops *)(dev)->driver->ops) + +/** + * panel_enable_backlight() - Enable the panel backlight + * + * @dev: Panel device containing the backlight to enable + * @return 0 if OK, -ve on error + */ +int panel_enable_backlight(struct udevice *dev); + +#endif

Most panels are very simple - they just have a power supply and a backlight. Add a driver which supports this and implements the enable_backlight() method.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/video/Makefile | 2 +- drivers/video/simple_panel.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 drivers/video/simple_panel.c
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index d29280c..6658e96 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -8,7 +8,7 @@ ifdef CONFIG_DM obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o obj-$(CONFIG_DM_VIDEO) += backlight-uclass.o -obj-$(CONFIG_DM_VIDEO) += panel-uclass.o +obj-$(CONFIG_DM_VIDEO) += panel-uclass.o simple_panel.o obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o obj-$(CONFIG_DM_VIDEO) += video_bmp.o ifdef CONFIG_DM_VIDEO diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c new file mode 100644 index 0000000..b161517 --- /dev/null +++ b/drivers/video/simple_panel.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <backlight.h> +#include <dm.h> +#include <panel.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct simple_panel_priv { + struct udevice *reg; + struct udevice *backlight; + struct gpio_desc enable; +}; + +static int simple_panel_enable_backlight(struct udevice *dev) +{ + struct simple_panel_priv *priv = dev_get_priv(dev); + int ret; + + dm_gpio_set_value(&priv->enable, 1); + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + return 0; +} + +static int simple_panel_ofdata_to_platdata(struct udevice *dev) +{ + struct simple_panel_priv *priv = dev_get_priv(dev); + int ret; + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "power-supply", &priv->reg); + if (ret) { + debug("%s: Warning: cnnot get power supply: ret=%d\n", + __func__, ret); + if (ret != -ENOENT) + return ret; + } + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + debug("%s: Cannot get backlight: ret=%d\n", __func__, ret); + return ret; + } + ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable, + GPIOD_IS_OUT); + if (ret) { + debug("%s: Warning: cannot get enable GPIO: ret=%d\n", + __func__, ret); + if (ret != -ENOENT) + return ret; + } + + return 0; +} + +static int simple_panel_probe(struct udevice *dev) +{ + struct simple_panel_priv *priv = dev_get_priv(dev); + int ret; + + if (priv->reg) { + debug("%s: Enable regulator '%s'\n", __func__, priv->reg->name); + ret = regulator_set_enable(priv->reg, true); + if (ret) + return ret; + } + + return 0; +} + +static const struct panel_ops simple_panel_ops = { + .enable_backlight = simple_panel_enable_backlight, +}; + +static const struct udevice_id simple_panel_ids[] = { + { .compatible = "simple-panel" }, + { } +}; + +U_BOOT_DRIVER(simple_panel) = { + .name = "simple_panel", + .id = UCLASS_PANEL, + .of_match = simple_panel_ids, + .ops = &simple_panel_ops, + .ofdata_to_platdata = simple_panel_ofdata_to_platdata, + .probe = simple_panel_probe, + .priv_auto_alloc_size = sizeof(struct simple_panel_priv), +};

The current DisplayPort uclass is too specific. The operations it provides are shared with other types of output devices, such as HDMI and LVDS LCD displays.
Generalise the uclass so that it can be used with these devices as well. Adjust the uclass to handle the EDID reading and conversion to display_timing internally.
Also update nyan-big which is affected by this.
Signed-off-by: Simon Glass sjg@chromium.org ---
configs/nyan-big_defconfig | 2 +- drivers/video/Kconfig | 13 +++++---- drivers/video/Makefile | 2 +- drivers/video/display-uclass.c | 52 ++++++++++++++++++++++++++++++++++++ drivers/video/dp-uclass.c | 34 ----------------------- drivers/video/tegra124/display.c | 18 ++++--------- drivers/video/tegra124/dp.c | 9 ++++--- include/{displayport.h => display.h} | 33 ++++++++++++++--------- include/dm/uclass-id.h | 2 +- include/edid.h | 1 + 10 files changed, 95 insertions(+), 71 deletions(-) create mode 100644 drivers/video/display-uclass.c delete mode 100644 drivers/video/dp-uclass.c rename include/{displayport.h => display.h} (59%)
diff --git a/configs/nyan-big_defconfig b/configs/nyan-big_defconfig index 7fc1bde..03348a6 100644 --- a/configs/nyan-big_defconfig +++ b/configs/nyan-big_defconfig @@ -25,7 +25,7 @@ CONFIG_TEGRA114_SPI=y CONFIG_TPM_TIS_INFINEON=y CONFIG_USB=y CONFIG_DM_USB=y -CONFIG_DISPLAY_PORT=y +CONFIG_DISPLAY=y CONFIG_VIDEO_TEGRA124=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_TPM=y diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index b90850b..1790fdd 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -293,12 +293,15 @@ config VIDEO_LCD_SPI_MISO option takes a string in the format understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H.
-config DISPLAY_PORT - bool "Enable DisplayPort support" +config DISPLAY + bool "Enable Display support" + depends on DM + default y help - eDP (Embedded DisplayPort) is a standard widely used in laptops - to drive LCD panels. This framework provides support for enabling - these displays where supported by the video hardware. + This supports drivers that provide a display, such as eDP (Embedded + DisplayPort) and HDMI (High Definition Multimedia Interface). + The devices provide a simple interface to start up the display, + read display information and enable it.
config VIDEO_SANDBOX_SDL bool "Enable sandbox video console using SDL" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6658e96..c135e22 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -6,7 +6,7 @@ #
ifdef CONFIG_DM -obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o +obj-$(CONFIG_DISPLAY) += display-uclass.o obj-$(CONFIG_DM_VIDEO) += backlight-uclass.o obj-$(CONFIG_DM_VIDEO) += panel-uclass.o simple_panel.o obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o diff --git a/drivers/video/display-uclass.c b/drivers/video/display-uclass.c new file mode 100644 index 0000000..31522ea --- /dev/null +++ b/drivers/video/display-uclass.c @@ -0,0 +1,52 @@ +/* + * Copyright 2014 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <display.h> +#include <edid.h> +#include <errno.h> + +int display_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + struct dm_display_ops *ops = display_get_ops(dev); + + if (!ops || !ops->read_edid) + return -ENOSYS; + return ops->read_edid(dev, buf, buf_size); +} + +int display_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *timing) +{ + struct dm_display_ops *ops = display_get_ops(dev); + + if (!ops || !ops->enable) + return -ENOSYS; + return ops->enable(dev, panel_bpp, timing); +} + +int display_read_timing(struct udevice *dev, struct display_timing *timing) +{ + struct dm_display_ops *ops = display_get_ops(dev); + int panel_bits_per_colour; + u8 buf[EDID_EXT_SIZE]; + int ret; + + if (!ops || !ops->read_edid) + return -ENOSYS; + ret = ops->read_edid(dev, buf, sizeof(buf)); + if (ret < 0) + return ret; + + return edid_get_timing(buf, ret, timing, &panel_bits_per_colour); +} + +UCLASS_DRIVER(display) = { + .id = UCLASS_DISPLAY, + .name = "display", + .per_device_platdata_auto_alloc_size = sizeof(struct display_plat), +}; diff --git a/drivers/video/dp-uclass.c b/drivers/video/dp-uclass.c deleted file mode 100644 index 17f5de9..0000000 --- a/drivers/video/dp-uclass.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <dm.h> -#include <displayport.h> -#include <errno.h> - -int display_port_read_edid(struct udevice *dev, u8 *buf, int buf_size) -{ - struct dm_display_port_ops *ops = display_port_get_ops(dev); - - if (!ops || !ops->read_edid) - return -ENOSYS; - return ops->read_edid(dev, buf, buf_size); -} - -int display_port_enable(struct udevice *dev, int panel_bpp, - const struct display_timing *timing) -{ - struct dm_display_port_ops *ops = display_port_get_ops(dev); - - if (!ops || !ops->enable) - return -ENOSYS; - return ops->enable(dev, panel_bpp, timing); -} - -UCLASS_DRIVER(display_port) = { - .id = UCLASS_DISPLAY_PORT, - .name = "display_port", -}; diff --git a/drivers/video/tegra124/display.c b/drivers/video/tegra124/display.c index 7179dbf..610ffa9 100644 --- a/drivers/video/tegra124/display.c +++ b/drivers/video/tegra124/display.c @@ -10,7 +10,7 @@ #include <dm.h> #include <edid.h> #include <errno.h> -#include <displayport.h> +#include <display.h> #include <edid.h> #include <fdtdec.h> #include <lcd.h> @@ -324,20 +324,12 @@ static int display_update_config_from_edid(struct udevice *dp_dev, int *panel_bppp, struct display_timing *timing) { - u8 buf[EDID_SIZE]; - int bpc, ret; + int ret;
- ret = display_port_read_edid(dp_dev, buf, sizeof(buf)); - if (ret < 0) - return ret; - ret = edid_get_timing(buf, ret, timing, &bpc); + ret = display_read_timing(dp_dev, timing); if (ret) return ret;
- /* Use this information if valid */ - if (bpc != -1) - *panel_bppp = bpc * 3; - return 0; }
@@ -398,7 +390,7 @@ int display_init(void *lcdbase, int fb_bits_per_pixel, int node; int ret;
- ret = uclass_get_device(UCLASS_DISPLAY_PORT, 0, &dp_dev); + ret = uclass_get_device(UCLASS_DISPLAY, 0, &dp_dev); if (ret) return ret;
@@ -450,7 +442,7 @@ int display_init(void *lcdbase, int fb_bits_per_pixel, }
/* Enable dp */ - ret = display_port_enable(dp_dev, panel_bpp, timing); + ret = display_enable(dp_dev, panel_bpp, timing); if (ret) return ret;
diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra124/dp.c index 1bf9202..bb1805a 100644 --- a/drivers/video/tegra124/dp.c +++ b/drivers/video/tegra124/dp.c @@ -6,16 +6,17 @@ */
#include <common.h> -#include <displayport.h> +#include <display.h> #include <dm.h> #include <div64.h> #include <errno.h> #include <fdtdec.h> #include <asm/io.h> #include <asm/arch-tegra/dc.h> -#include "displayport.h" +#include "display.h" #include "edid.h" #include "sor.h" +#include "displayport.h"
DECLARE_GLOBAL_DATA_PTR;
@@ -1573,7 +1574,7 @@ static int tegra_dp_read_edid(struct udevice *dev, u8 *buf, int buf_size) buf_size, &aux_stat); }
-static const struct dm_display_port_ops dp_tegra_ops = { +static const struct dm_display_ops dp_tegra_ops = { .read_edid = tegra_dp_read_edid, .enable = tegra_dp_enable, }; @@ -1596,7 +1597,7 @@ static const struct udevice_id tegra_dp_ids[] = {
U_BOOT_DRIVER(dp_tegra) = { .name = "dpaux_tegra", - .id = UCLASS_DISPLAY_PORT, + .id = UCLASS_DISPLAY, .of_match = tegra_dp_ids, .ofdata_to_platdata = tegra_dp_ofdata_to_platdata, .probe = dp_tegra_probe, diff --git a/include/displayport.h b/include/display.h similarity index 59% rename from include/displayport.h rename to include/display.h index f7c7e25..c180e76 100644 --- a/include/displayport.h +++ b/include/display.h @@ -4,21 +4,31 @@ * SPDX-License-Identifier: GPL-2.0+ */
-#ifndef _DISPLAYPORT_H -#define _DISPLAYPORT_H +#ifndef _DISPLAY_H +#define _DISPLAY_H
struct udevice; struct display_timing;
/** - * display_port_read_edid() - Read information from EDID + * Display uclass platform data for each device + * + * @source_id: ID for the source of the display data, typically a video + * controller + * @src_dev: Source device providing the video + */ +struct display_plat { + int source_id; + struct udevice *src_dev; +}; + +/** + * display_read_timing() - Read timing information from EDID * * @dev: Device to read from - * @buf: Buffer to read into (should be EDID_SIZE bytes) - * @buf_size: Buffer size (should be EDID_SIZE) - * @return number of bytes read, <=0 for error + * @return 0 if OK, -ve on error */ -int display_port_read_edid(struct udevice *dev, u8 *buf, int buf_size); +int display_read_timing(struct udevice *dev, struct display_timing *timing);
/** * display_port_enable() - Enable a display port device @@ -28,10 +38,10 @@ int display_port_read_edid(struct udevice *dev, u8 *buf, int buf_size); * @timing: Display timings * @return 0 if OK, -ve on error */ -int display_port_enable(struct udevice *dev, int panel_bpp, - const struct display_timing *timing); +int display_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *timing);
-struct dm_display_port_ops { +struct dm_display_ops { /** * read_edid() - Read information from EDID * @@ -54,7 +64,6 @@ struct dm_display_port_ops { const struct display_timing *timing); };
-#define display_port_get_ops(dev) \ - ((struct dm_display_port_ops *)(dev)->driver->ops) +#define display_get_ops(dev) ((struct dm_display_ops *)(dev)->driver->ops)
#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 8308c23..8391e38 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -29,7 +29,7 @@ enum uclass_id { UCLASS_CLK, /* Clock source, e.g. used by peripherals */ UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_CROS_EC, /* Chrome OS EC */ - UCLASS_DISPLAY_PORT, /* Display port video */ + UCLASS_DISPLAY, /* Display (e.g. DisplayPort, HDMI) */ UCLASS_RAM, /* RAM controller */ UCLASS_ETH, /* Ethernet device */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ diff --git a/include/edid.h b/include/edid.h index 88b4b7d..8b022fa 100644 --- a/include/edid.h +++ b/include/edid.h @@ -17,6 +17,7 @@
/* Size of the EDID data */ #define EDID_SIZE 128 +#define EDID_EXT_SIZE 256
#define GET_BIT(_x, _pos) \ (((_x) >> (_pos)) & 1)

These should match the datasheet naming. Adjust them.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/include/asm/arch-rockchip/cru_rk3288.h | 58 ++++++++++++------------- drivers/clk/clk_rk3288.c | 39 ++++------------- 2 files changed, 38 insertions(+), 59 deletions(-)
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3288.h b/arch/arm/include/asm/arch-rockchip/cru_rk3288.h index 7ebcc40..b0dea70 100644 --- a/arch/arm/include/asm/arch-rockchip/cru_rk3288.h +++ b/arch/arm/include/asm/arch-rockchip/cru_rk3288.h @@ -131,35 +131,35 @@ enum {
/* CRU_MODE_CON */ enum { - NPLL_WORK_SHIFT = 0xe, - NPLL_WORK_MASK = 3, - NPLL_WORK_SLOW = 0, - NPLL_WORK_NORMAL, - NPLL_WORK_DEEP, - - GPLL_WORK_SHIFT = 0xc, - GPLL_WORK_MASK = 3, - GPLL_WORK_SLOW = 0, - GPLL_WORK_NORMAL, - GPLL_WORK_DEEP, - - CPLL_WORK_SHIFT = 8, - CPLL_WORK_MASK = 3, - CPLL_WORK_SLOW = 0, - CPLL_WORK_NORMAL, - CPLL_WORK_DEEP, - - DPLL_WORK_SHIFT = 4, - DPLL_WORK_MASK = 3, - DPLL_WORK_SLOW = 0, - DPLL_WORK_NORMAL, - DPLL_WORK_DEEP, - - APLL_WORK_SHIFT = 0, - APLL_WORK_MASK = 3, - APLL_WORK_SLOW = 0, - APLL_WORK_NORMAL, - APLL_WORK_DEEP, + NPLL_MODE_SHIFT = 0xe, + NPLL_MODE_MASK = 3, + NPLL_MODE_SLOW = 0, + NPLL_MODE_NORMAL, + NPLL_MODE_DEEP, + + GPLL_MODE_SHIFT = 0xc, + GPLL_MODE_MASK = 3, + GPLL_MODE_SLOW = 0, + GPLL_MODE_NORMAL, + GPLL_MODE_DEEP, + + CPLL_MODE_SHIFT = 8, + CPLL_MODE_MASK = 3, + CPLL_MODE_SLOW = 0, + CPLL_MODE_NORMAL, + CPLL_MODE_DEEP, + + DPLL_MODE_SHIFT = 4, + DPLL_MODE_MASK = 3, + DPLL_MODE_SLOW = 0, + DPLL_MODE_NORMAL, + DPLL_MODE_DEEP, + + APLL_MODE_SHIFT = 0, + APLL_MODE_MASK = 3, + APLL_MODE_SLOW = 0, + APLL_MODE_NORMAL, + APLL_MODE_DEEP, };
/* CRU_APLL_CON0 */ diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c index f25a124..d3c14dd 100644 --- a/drivers/clk/clk_rk3288.c +++ b/drivers/clk/clk_rk3288.c @@ -97,27 +97,6 @@ enum { PERI_ACLK_DIV_SHIFT = 0, PERI_ACLK_DIV_MASK = 0x1f,
- /* CLKSEL37 */ - DPLL_MODE_MASK = 0x3, - DPLL_MODE_SHIFT = 4, - DPLL_MODE_SLOW = 0, - DPLL_MODE_NORM, - - CPLL_MODE_MASK = 3, - CPLL_MODE_SHIFT = 8, - CPLL_MODE_SLOW = 0, - CPLL_MODE_NORM, - - GPLL_MODE_MASK = 3, - GPLL_MODE_SHIFT = 12, - GPLL_MODE_SLOW = 0, - GPLL_MODE_NORM, - - NPLL_MODE_MASK = 3, - NPLL_MODE_SHIFT = 14, - NPLL_MODE_SLOW = 0, - NPLL_MODE_NORM, - SOCSTS_DPLL_LOCK = 1 << 5, SOCSTS_APLL_LOCK = 1 << 6, SOCSTS_CPLL_LOCK = 1 << 7, @@ -251,7 +230,7 @@ static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf,
/* PLL enter normal-mode */ rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, - DPLL_MODE_NORM << DPLL_MODE_SHIFT); + DPLL_MODE_NORMAL << DPLL_MODE_SHIFT);
return 0; } @@ -331,8 +310,8 @@ static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) rk_clrsetreg(&cru->cru_mode_con, GPLL_MODE_MASK << GPLL_MODE_SHIFT | CPLL_MODE_MASK << CPLL_MODE_SHIFT, - GPLL_MODE_NORM << GPLL_MODE_SHIFT | - GPLL_MODE_NORM << CPLL_MODE_SHIFT); + GPLL_MODE_NORMAL << GPLL_MODE_SHIFT | + CPLL_MODE_NORMAL << CPLL_MODE_SHIFT); } #endif
@@ -345,17 +324,17 @@ static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru, int pll_id = rk_pll_id(clk_id); struct rk3288_pll *pll = &cru->pll[pll_id]; static u8 clk_shift[CLK_COUNT] = { - 0xff, APLL_WORK_SHIFT, DPLL_WORK_SHIFT, CPLL_WORK_SHIFT, - GPLL_WORK_SHIFT, NPLL_WORK_SHIFT + 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT, + GPLL_MODE_SHIFT, NPLL_MODE_SHIFT }; uint shift;
con = readl(&cru->cru_mode_con); shift = clk_shift[clk_id]; - switch ((con >> shift) & APLL_WORK_MASK) { - case APLL_WORK_SLOW: + switch ((con >> shift) & APLL_MODE_MASK) { + case APLL_MODE_SLOW: return OSC_HZ; - case APLL_WORK_NORMAL: + case APLL_MODE_NORMAL: /* normal mode */ con = readl(&pll->con0); no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1; @@ -364,7 +343,7 @@ static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru, nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1;
return (24 * nf / (nr * no)) * 1000000; - case APLL_WORK_DEEP: + case APLL_MODE_DEEP: default: return 32768; }

The displays need to use NPLL and also select some new peripheral clocks. Add support for these to the clock driver.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/clk/clk_rk3288.c | 174 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 170 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c index d3c14dd..42d2f85 100644 --- a/drivers/clk/clk_rk3288.c +++ b/drivers/clk/clk_rk3288.c @@ -235,6 +235,124 @@ static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, return 0; }
+#ifndef CONFIG_SPL_BUILD +#define VCO_MAX_KHZ 2200000 +#define VCO_MIN_KHZ 440000 +#define FREF_MAX_KHZ 2200000 +#define FREF_MIN_KHZ 269 + +static int pll_para_config(ulong freq_hz, struct pll_div *div, uint *ext_div) +{ + uint ref_khz = OSC_HZ / 1000, nr, nf = 0; + uint fref_khz; + uint diff_khz, best_diff_khz; + const uint max_nr = 1 << 6, max_nf = 1 << 12, max_no = 1 << 4; + uint vco_khz; + uint no = 1; + uint freq_khz = freq_hz / 1000; + + if (!freq_hz) { + printf("%s: the frequency can not be 0 Hz\n", __func__); + return -EINVAL; + } + + no = DIV_ROUND_UP(VCO_MIN_KHZ, freq_khz); + if (ext_div) { + *ext_div = DIV_ROUND_UP(no, max_no); + no = DIV_ROUND_UP(no, *ext_div); + } + + /* only even divisors (and 1) are supported */ + if (no > 1) + no = DIV_ROUND_UP(no, 2) * 2; + + vco_khz = freq_khz * no; + if (ext_div) + vco_khz *= *ext_div; + + if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || no > max_no) { + printf("%s: Cannot find out a supported VCO for Frequency (%luHz).\n", + __func__, freq_hz); + return -1; + } + + div->no = no; + + best_diff_khz = vco_khz; + for (nr = 1; nr < max_nr && best_diff_khz; nr++) { + fref_khz = ref_khz / nr; + if (fref_khz < FREF_MIN_KHZ) + break; + if (fref_khz > FREF_MAX_KHZ) + continue; + + nf = vco_khz / fref_khz; + if (nf >= max_nf) + continue; + diff_khz = vco_khz - nf * fref_khz; + if (nf + 1 < max_nf && diff_khz > fref_khz / 2) { + nf++; + diff_khz = fref_khz - diff_khz; + } + + if (diff_khz >= best_diff_khz) + continue; + + best_diff_khz = diff_khz; + div->nr = nr; + div->nf = nf; + } + + if (best_diff_khz > 4 * 1000) { + printf("%s: Failed to match output frequency %lu, difference is %u Hz, exceed 4MHZ\n", + __func__, freq_hz, best_diff_khz * 1000); + return -EINVAL; + } + + return 0; +} + +static int rockchip_vop_set_clk(struct rk3288_cru *cru, struct rk3288_grf *grf, + int periph, unsigned int rate_hz) +{ + struct pll_div npll_config = {0}; + u32 lcdc_div; + int ret; + + ret = pll_para_config(rate_hz, &npll_config, &lcdc_div); + if (ret) + return ret; + + rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT, + NPLL_MODE_SLOW << NPLL_MODE_SHIFT); + rkclk_set_pll(cru, CLK_NEW, &npll_config); + + /* waiting for pll lock */ + while (1) { + if (readl(&grf->soc_status[1]) & SOCSTS_NPLL_LOCK) + break; + udelay(1); + } + + rk_clrsetreg(&cru->cru_mode_con, NPLL_MODE_MASK << NPLL_MODE_SHIFT, + NPLL_MODE_NORMAL << NPLL_MODE_SHIFT); + + /* vop dclk source clk: npll,dclk_div: 1 */ + switch (periph) { + case DCLK_VOP0: + rk_clrsetreg(&cru->cru_clksel_con[27], 0xff << 8 | 3 << 0, + (lcdc_div - 1) << 8 | 2 << 0); + break; + case DCLK_VOP1: + rk_clrsetreg(&cru->cru_clksel_con[29], 0xff << 8 | 3 << 6, + (lcdc_div - 1) << 8 | 2 << 6); + break; + } + + return 0; +} +#endif + #ifdef CONFIG_SPL_BUILD static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) { @@ -559,6 +677,7 @@ ulong rk3288_get_periph_rate(struct udevice *dev, int periph) ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate) { struct rk3288_clk_priv *priv = dev_get_priv(dev); + struct rk3288_cru *cru = priv->cru; struct udevice *gclk; ulong new_rate, gclk_rate; int ret; @@ -571,15 +690,62 @@ ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate) case HCLK_EMMC: case HCLK_SDMMC: case HCLK_SDIO0: - new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate, periph, - rate); + new_rate = rockchip_mmc_set_clk(cru, gclk_rate, periph, rate); break; case SCLK_SPI0: case SCLK_SPI1: case SCLK_SPI2: - new_rate = rockchip_spi_set_clk(priv->cru, gclk_rate, periph, - rate); + new_rate = rockchip_spi_set_clk(cru, gclk_rate, periph, rate); break; +#ifndef CONFIG_SPL_BUILD + case DCLK_VOP0: + case DCLK_VOP1: + new_rate = rockchip_vop_set_clk(cru, priv->grf, periph, rate); + break; + case SCLK_EDP_24M: + /* clk_edp_24M source: 24M */ + rk_setreg(&cru->cru_clksel_con[28], 1 << 15); + + /* rst edp */ + rk_setreg(&cru->cru_clksel_con[6], 1 << 15); + udelay(1); + rk_clrreg(&cru->cru_clksel_con[6], 1 << 15); + new_rate = rate; + break; + case ACLK_VOP0: + case ACLK_VOP1: { + u32 div; + + /* vop aclk source clk: cpll */ + div = CPLL_HZ / rate; + assert((div - 1 < 64) && (div * rate == CPLL_HZ)); + + switch (periph) { + case ACLK_VOP0: + rk_clrsetreg(&cru->cru_clksel_con[31], + 3 << 6 | 0x1f << 0, + 0 << 6 | (div - 1) << 0); + break; + case ACLK_VOP1: + rk_clrsetreg(&cru->cru_clksel_con[31], + 3 << 14 | 0x1f << 8, + 0 << 14 | (div - 1) << 8); + break; + } + new_rate = rate; + break; + } + case PCLK_HDMI_CTRL: + /* enable pclk hdmi ctrl */ + rk_clrreg(&cru->cru_clkgate_con[16], 1 << 9); + + /* software reset hdmi */ + rk_setreg(&cru->cru_clkgate_con[7], 1 << 9); + udelay(1); + rk_clrreg(&cru->cru_clkgate_con[7], 1 << 9); + new_rate = rate; + break; +#endif default: return -ENOENT; }

Some Rockchip SoCs support HDMI output. Add a display driver for this so that these displays can be used on supported boards.
Unfortunately this driver is not fully functional. It cannot reliably read EDID information over HDMI. This seems to be due to the clocks being incorrect - the I2C bus speed appears to be up to 100x slower than the clock settings indicate. The root cause may be in the clock logic.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/include/asm/arch-rockchip/hdmi_rk3288.h | 456 +++++++++++ drivers/video/Kconfig | 10 + drivers/video/Makefile | 1 + drivers/video/rockchip/Makefile | 8 + drivers/video/rockchip/rk_hdmi.c | 929 +++++++++++++++++++++++ 5 files changed, 1404 insertions(+) create mode 100644 arch/arm/include/asm/arch-rockchip/hdmi_rk3288.h create mode 100644 drivers/video/rockchip/Makefile create mode 100644 drivers/video/rockchip/rk_hdmi.c
diff --git a/arch/arm/include/asm/arch-rockchip/hdmi_rk3288.h b/arch/arm/include/asm/arch-rockchip/hdmi_rk3288.h new file mode 100644 index 0000000..0b51d40 --- /dev/null +++ b/arch/arm/include/asm/arch-rockchip/hdmi_rk3288.h @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2015 Google, Inc + * Copyright 2014 Rockchip Inc. + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ASM_ARCH_HDMI_H +#define _ASM_ARCH_HDMI_H + + +#define HDMI_EDID_BLOCK_SIZE 128 + +struct rk3288_hdmi { + u32 reserved0[0x100]; + u32 ih_fc_stat0; + u32 ih_fc_stat1; + u32 ih_fc_stat2; + u32 ih_as_stat0; + u32 ih_phy_stat0; + u32 ih_i2cm_stat0; + u32 ih_cec_stat0; + u32 ih_vp_stat0; + u32 ih_i2cmphy_stat0; + u32 ih_ahbdmaaud_stat0; + u32 reserved1[0x17f-0x109]; + u32 ih_mute_fc_stat0; + u32 ih_mute_fc_stat1; + u32 ih_mute_fc_stat2; + u32 ih_mute_as_stat0; + u32 ih_mute_phy_stat0; + u32 ih_mute_i2cm_stat0; + u32 ih_mute_cec_stat0; + u32 ih_mute_vp_stat0; + u32 ih_mute_i2cmphy_stat0; + u32 ih_mute_ahbdmaaud_stat0; + u32 reserved2[0x1fe - 0x189]; + u32 ih_mute; + u32 tx_invid0; + u32 tx_instuffing; + u32 tx_gydata0; + u32 tx_gydata1; + u32 tx_rcrdata0; + u32 tx_rcrdata1; + u32 tx_bcbdata0; + u32 tx_bcbdata1; + u32 reserved3[0x7ff-0x207]; + u32 vp_status; + u32 vp_pr_cd; + u32 vp_stuff; + u32 vp_remap; + u32 vp_conf; + u32 vp_stat; + u32 vp_int; + u32 vp_mask; + u32 vp_pol; + u32 reserved4[0xfff-0x808]; + u32 fc_invidconf; + u32 fc_inhactv0; + u32 fc_inhactv1; + u32 fc_inhblank0; + u32 fc_inhblank1; + u32 fc_invactv0; + u32 fc_invactv1; + u32 fc_invblank; + u32 fc_hsyncindelay0; + u32 fc_hsyncindelay1; + u32 fc_hsyncinwidth0; + u32 fc_hsyncinwidth1; + u32 fc_vsyncindelay; + u32 fc_vsyncinwidth; + u32 fc_infreq0; + u32 fc_infreq1; + u32 fc_infreq2; + u32 fc_ctrldur; + u32 fc_exctrldur; + u32 fc_exctrlspac; + u32 fc_ch0pream; + u32 fc_ch1pream; + u32 fc_ch2pream; + u32 fc_aviconf3; + u32 fc_gcp; + u32 fc_aviconf0; + u32 fc_aviconf1; + u32 fc_aviconf2; + u32 fc_avivid; + u32 fc_avietb0; + u32 fc_avietb1; + u32 fc_avisbb0; + u32 fc_avisbb1; + u32 fc_avielb0; + u32 fc_avielb1; + u32 fc_avisrb0; + u32 fc_avisrb1; + u32 fc_audiconf0; + u32 fc_audiconf1; + u32 fc_audiconf2; + u32 fc_audiconf3; + u32 fc_vsdieeeid0; + u32 fc_vsdsize; + u32 reserved7[0x2fff-0x102a]; + u32 phy_conf0; + u32 phy_tst0; + u32 phy_tst1; + u32 phy_tst2; + u32 phy_stat0; + u32 phy_int0; + u32 phy_mask0; + u32 phy_pol0; + u32 reserved8[0x301f-0x3007]; + u32 phy_i2cm_slave_addr; + u32 phy_i2cm_address_addr; + u32 phy_i2cm_datao_1_addr; + u32 phy_i2cm_datao_0_addr; + u32 phy_i2cm_datai_1_addr; + u32 phy_i2cm_datai_0_addr; + u32 phy_i2cm_operation_addr; + u32 phy_i2cm_int_addr; + u32 phy_i2cm_ctlint_addr; + u32 phy_i2cm_div_addr; + u32 phy_i2cm_softrstz_addr; + u32 phy_i2cm_ss_scl_hcnt_1_addr; + u32 phy_i2cm_ss_scl_hcnt_0_addr; + u32 phy_i2cm_ss_scl_lcnt_1_addr; + u32 phy_i2cm_ss_scl_lcnt_0_addr; + u32 phy_i2cm_fs_scl_hcnt_1_addr; + u32 phy_i2cm_fs_scl_hcnt_0_addr; + u32 phy_i2cm_fs_scl_lcnt_1_addr; + u32 phy_i2cm_fs_scl_lcnt_0_addr; + u32 reserved9[0x30ff-0x3032]; + u32 aud_conf0; + u32 aud_conf1; + u32 aud_int; + u32 aud_conf2; + u32 aud_int1; + u32 reserved32[0x31ff-0x3104]; + u32 aud_n1; + u32 aud_n2; + u32 aud_n3; + u32 aud_cts1; + u32 aud_cts2; + u32 aud_cts3; + u32 aud_inputclkfs; + u32 reserved12[0x3fff-0x3206]; + u32 mc_sfrdiv; + u32 mc_clkdis; + u32 mc_swrstz; + u32 mc_opctrl; + u32 mc_flowctrl; + u32 mc_phyrstz; + u32 mc_lockonclock; + u32 mc_heacphy_rst; + u32 reserved13[0x40ff-0x4007]; + u32 csc_cfg; + u32 csc_scale; + struct { + u32 msb; + u32 lsb; + } csc_coef[3][4]; + u32 reserved17[0x7dff-0x4119]; + u32 i2cm_slave; + u32 i2c_address; + u32 i2cm_datao; + u32 i2cm_datai; + u32 i2cm_operation; + u32 i2cm_int; + u32 i2cm_ctlint; + u32 i2cm_div; + u32 i2cm_segaddr; + u32 i2cm_softrstz; + u32 i2cm_segptr; + u32 i2cm_ss_scl_hcnt_1_addr; + u32 i2cm_ss_scl_hcnt_0_addr; + u32 i2cm_ss_scl_lcnt_1_addr; + u32 i2cm_ss_scl_lcnt_0_addr; + u32 i2cm_fs_scl_hcnt_1_addr; + u32 i2cm_fs_scl_hcnt_0_addr; + u32 i2cm_fs_scl_lcnt_1_addr; + u32 i2cm_fs_scl_lcnt_0_addr; + u32 reserved18[0x7e1f-0x7e12]; + u32 i2cm_buf0; +}; +check_member(rk3288_hdmi, i2cm_buf0, 0x1f880); + +enum { + /* HDMI PHY registers define */ + PHY_OPMODE_PLLCFG = 0x06, + PHY_CKCALCTRL = 0x05, + PHY_CKSYMTXCTRL = 0x09, + PHY_VLEVCTRL = 0x0e, + PHY_PLLCURRCTRL = 0x10, + PHY_PLLPHBYCTRL = 0x13, + PHY_PLLGMPCTRL = 0x15, + PHY_PLLCLKBISTPHASE = 0x17, + PHY_TXTERM = 0x19, + + /* ih_phy_stat0 field values */ + HDMI_IH_PHY_STAT0_HPD = 0x1, + + /* ih_mute field values */ + HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT = 0x2, + HDMI_IH_MUTE_MUTE_ALL_INTERRUPT = 0x1, + + /* tx_invid0 field values */ + HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE = 0x00, + HDMI_TX_INVID0_VIDEO_MAPPING_MASK = 0x1f, + HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET = 0, + + /* tx_instuffing field values */ + HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE = 0x4, + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE = 0x2, + HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE = 0x1, + + /* vp_pr_cd field values */ + HDMI_VP_PR_CD_COLOR_DEPTH_MASK = 0xf0, + HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET = 4, + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK = 0x0f, + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET = 0, + + /* vp_stuff field values */ + HDMI_VP_STUFF_IDEFAULT_PHASE_MASK = 0x20, + HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET = 5, + HDMI_VP_STUFF_YCC422_STUFFING_MASK = 0x4, + HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE = 0x4, + HDMI_VP_STUFF_PP_STUFFING_MASK = 0x2, + HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE = 0x2, + HDMI_VP_STUFF_PR_STUFFING_MASK = 0x1, + HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE = 0x1, + + /* vp_conf field values */ + HDMI_VP_CONF_BYPASS_EN_MASK = 0x40, + HDMI_VP_CONF_BYPASS_EN_ENABLE = 0x40, + HDMI_VP_CONF_PP_EN_ENMASK = 0x20, + HDMI_VP_CONF_PP_EN_DISABLE = 0x00, + HDMI_VP_CONF_PR_EN_MASK = 0x10, + HDMI_VP_CONF_PR_EN_DISABLE = 0x00, + HDMI_VP_CONF_YCC422_EN_MASK = 0x8, + HDMI_VP_CONF_YCC422_EN_DISABLE = 0x0, + HDMI_VP_CONF_BYPASS_SELECT_MASK = 0x4, + HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER = 0x4, + HDMI_VP_CONF_OUTPUT_SELECTOR_MASK = 0x3, + HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS = 0x3, + + /* vp_remap field values */ + HDMI_VP_REMAP_YCC422_16BIT = 0x0, + + /* fc_invidconf field values */ + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK = 0x80, + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE = 0x80, + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE = 0x00, + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_MASK = 0x40, + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH = 0x40, + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW = 0x00, + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_MASK = 0x20, + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH = 0x20, + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW = 0x00, + HDMI_FC_INVIDCONF_DE_IN_POLARITY_MASK = 0x10, + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH = 0x10, + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW = 0x00, + HDMI_FC_INVIDCONF_DVI_MODEZ_MASK = 0x8, + HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE = 0x8, + HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE = 0x0, + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_MASK = 0x2, + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH = 0x2, + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW = 0x0, + HDMI_FC_INVIDCONF_IN_I_P_MASK = 0x1, + HDMI_FC_INVIDCONF_IN_I_P_INTERLACED = 0x1, + HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE = 0x0, + + + /* fc_aviconf0-fc_aviconf3 field values */ + HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03, + HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00, + HDMI_FC_AVICONF0_PIX_FMT_YCBCR422 = 0x01, + HDMI_FC_AVICONF0_PIX_FMT_YCBCR444 = 0x02, + HDMI_FC_AVICONF0_ACTIVE_FMT_MASK = 0x40, + HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT = 0x40, + HDMI_FC_AVICONF0_ACTIVE_FMT_NO_INFO = 0x00, + HDMI_FC_AVICONF0_BAR_DATA_MASK = 0x0c, + HDMI_FC_AVICONF0_BAR_DATA_NO_DATA = 0x00, + HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR = 0x04, + HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR = 0x08, + HDMI_FC_AVICONF0_BAR_DATA_VERT_HORIZ_BAR = 0x0c, + HDMI_FC_AVICONF0_SCAN_INFO_MASK = 0x30, + HDMI_FC_AVICONF0_SCAN_INFO_OVERSCAN = 0x10, + HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN = 0x20, + HDMI_FC_AVICONF0_SCAN_INFO_NODATA = 0x00, + + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_MASK = 0x0f, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_USE_CODED = 0x08, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3 = 0x09, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9 = 0x0a, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_14_9 = 0x0b, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_MASK = 0x30, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_NO_DATA = 0x00, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3 = 0x10, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9 = 0x20, + HDMI_FC_AVICONF1_COLORIMETRY_MASK = 0xc0, + HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA = 0x00, + HDMI_FC_AVICONF1_COLORIMETRY_SMPTE = 0x40, + HDMI_FC_AVICONF1_COLORIMETRY_ITUR = 0x80, + HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO = 0xc0, + + HDMI_FC_AVICONF2_SCALING_MASK = 0x03, + HDMI_FC_AVICONF2_SCALING_NONE = 0x00, + HDMI_FC_AVICONF2_SCALING_HORIZ = 0x01, + HDMI_FC_AVICONF2_SCALING_VERT = 0x02, + HDMI_FC_AVICONF2_SCALING_HORIZ_vert = 0x03, + HDMI_FC_AVICONF2_RGB_QUANT_MASK = 0x0c, + HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT = 0x00, + HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE = 0x04, + HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE = 0x08, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_MASK = 0x70, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601 = 0x00, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709 = 0x10, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_SYCC601 = 0x20, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_YCC601 = 0x30, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_RGB = 0x40, + HDMI_FC_AVICONF2_IT_CONTENT_MASK = 0x80, + HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA = 0x00, + HDMI_FC_AVICONF2_IT_CONTENT_VALID = 0x80, + + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_MASK = 0x03, + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS = 0x00, + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_PHOTO = 0x01, + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_CINEMA = 0x02, + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GAME = 0x03, + HDMI_FC_AVICONF3_QUANT_RANGE_MASK = 0x0c, + HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00, + HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04, + + /* fc_gcp field values*/ + HDMI_FC_GCP_SET_AVMUTE = 0x02, + HDMI_FC_GCP_CLEAR_AVMUTE = 0x01, + + /* phy_conf0 field values */ + HDMI_PHY_CONF0_PDZ_MASK = 0x80, + HDMI_PHY_CONF0_PDZ_OFFSET = 7, + HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, + HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, + HDMI_PHY_CONF0_SPARECTRL_MASK = 0x20, + HDMI_PHY_CONF0_SPARECTRL_OFFSET = 5, + HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10, + HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4, + HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, + HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3, + HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2, + HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1, + HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1, + HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0, + + /* phy_tst0 field values */ + HDMI_PHY_TST0_TSTCLR_MASK = 0x20, + HDMI_PHY_TST0_TSTCLR_OFFSET = 5, + + /* phy_stat0 field values */ + HDMI_PHY_HPD = 0x02, + HDMI_PHY_TX_PHY_LOCK = 0x01, + + /* phy_i2cm_slave_addr field values */ + HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69, + + /* phy_i2cm_operation_addr field values */ + HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10, + + /* hdmi_phy_i2cm_int_addr */ + HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08, + + /* hdmi_phy_i2cm_ctlint_addr */ + HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80, + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08, + + /* aud_conf0 field values */ + HDMI_AUD_CONF0_SW_AUDIO_FIFO_RST = 0x80, + HDMI_AUD_CONF0_I2S_SELECT = 0x20, + HDMI_AUD_CONF0_I2S_IN_EN_0 = 0x01, + HDMI_AUD_CONF0_I2S_IN_EN_1 = 0x02, + HDMI_AUD_CONF0_I2S_IN_EN_2 = 0x04, + HDMI_AUD_CONF0_I2S_IN_EN_3 = 0x08, + + /* aud_conf0 field values */ + HDMI_AUD_CONF1_I2S_MODE_STANDARD_MODE = 0x0, + HDMI_AUD_CONF1_I2S_WIDTH_16BIT = 0x10, + + /* aud_n3 field values */ + HDMI_AUD_N3_NCTS_ATOMIC_WRITE = 0x80, + HDMI_AUD_N3_AUDN19_16_MASK = 0x0f, + + /* aud_cts3 field values */ + HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5, + HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0, + HDMI_AUD_CTS3_N_SHIFT_1 = 0, + HDMI_AUD_CTS3_N_SHIFT_16 = 0x20, + HDMI_AUD_CTS3_N_SHIFT_32 = 0x40, + HDMI_AUD_CTS3_N_SHIFT_64 = 0x60, + HDMI_AUD_CTS3_N_SHIFT_128 = 0x80, + HDMI_AUD_CTS3_N_SHIFT_256 = 0xa0, + HDMI_AUD_CTS3_CTS_MANUAL = 0x10, + HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f, + + /* aud_inputclkfs filed values */ + HDMI_AUD_INPUTCLKFS_128 = 0x0, + + /* mc_clkdis field values */ + HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8, + HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2, + HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1, + + /* mc_swrstz field values */ + HDMI_MC_SWRSTZ_II2SSWRST_REQ = 0x08, + HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02, + + /* mc_flowctrl field values */ + HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH = 0x1, + HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0, + + /* mc_phyrstz field values */ + HDMI_MC_PHYRSTZ_ASSERT = 0x0, + HDMI_MC_PHYRSTZ_DEASSERT = 0x1, + + /* mc_heacphy_rst field values */ + HDMI_MC_HEACPHY_RST_ASSERT = 0x1, + + /* csc_cfg field values */ + HDMI_CSC_CFG_INTMODE_DISABLE = 0x00, + + /* csc_scale field values */ + HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK = 0xf0, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP = 0x00, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP = 0x50, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP = 0x60, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP = 0x70, + HDMI_CSC_SCALE_CSCSCALE_MASK = 0x03, + + /* i2cm filed values */ + HDMI_I2CM_SLAVE_DDC_ADDR = 0x50, + HDMI_I2CM_SEGADDR_DDC = 0x30, + HDMI_I2CM_OPT_RD8_EXT = 0x8, + HDMI_I2CM_OPT_RD8 = 0x4, + HDMI_I2CM_DIV_FAST_STD_MODE = 0x8, + HDMI_I2CM_DIV_FAST_MODE = 0x8, + HDMI_I2CM_DIV_STD_MODE = 0x0, + HDMI_I2CM_SOFTRSTZ = 0x1, +}; + +/* +struct display_timing; +struct rk3288_grf; + +int rk_hdmi_init(struct rk3288_grf *grf, u32 vop_id); +int rk_hdmi_enable(const struct display_timing *edid); +int rk_hdmi_get_edid(struct rk3288_grf *grf, struct display_timing *edid); +*/ + +#endif diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 1790fdd..c23ab00 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -303,6 +303,16 @@ config DISPLAY The devices provide a simple interface to start up the display, read display information and enable it.
+config VIDEO_ROCKCHIP + bool "Enable Rockchip video support" + depends on DM_VIDEO + help + Rockchip SoCs provide video output capabilities for High-Definition + Multimedia Interface (HDMI), Low-voltage Differential Signalling + (LVDS), embedded DisplayPort (eDP) and Display Serial Interface + (DSI). This driver supports the on-chip video output device, and + targets the Rockchip RK3288. + config VIDEO_SANDBOX_SDL bool "Enable sandbox video console using SDL" depends on SANDBOX diff --git a/drivers/video/Makefile b/drivers/video/Makefile index c135e22..0910510 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -62,5 +62,6 @@ obj-$(CONFIG_LG4573) += lg4573.o obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ +obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/
obj-y += bridge/ diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile new file mode 100644 index 0000000..361fcb5 --- /dev/null +++ b/drivers/video/rockchip/Makefile @@ -0,0 +1,8 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += rk_hdmi.o diff --git a/drivers/video/rockchip/rk_hdmi.c b/drivers/video/rockchip/rk_hdmi.c new file mode 100644 index 0000000..66b238a --- /dev/null +++ b/drivers/video/rockchip/rk_hdmi.c @@ -0,0 +1,929 @@ +/* + * Copyright (c) 2015 Google, Inc + * Copyright 2014 Rockchip Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <display.h> +#include <dm.h> +#include <edid.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/grf_rk3288.h> +#include <asm/arch/hdmi_rk3288.h> +#include <power/regulator.h> + +struct tmds_n_cts { + u32 tmds; + u32 cts; + u32 n; +}; + +struct rk_hdmi_priv { + struct rk3288_hdmi *regs; + struct rk3288_grf *grf; +}; + +static const struct tmds_n_cts n_cts_table[] = { + { + .tmds = 25175, .n = 6144, .cts = 25175, + }, { + .tmds = 25200, .n = 6144, .cts = 25200, + }, { + .tmds = 27000, .n = 6144, .cts = 27000, + }, { + .tmds = 27027, .n = 6144, .cts = 27027, + }, { + .tmds = 40000, .n = 6144, .cts = 40000, + }, { + .tmds = 54000, .n = 6144, .cts = 54000, + }, { + .tmds = 54054, .n = 6144, .cts = 54054, + }, { + .tmds = 65000, .n = 6144, .cts = 65000, + }, { + .tmds = 74176, .n = 11648, .cts = 140625, + }, { + .tmds = 74250, .n = 6144, .cts = 74250, + }, { + .tmds = 83500, .n = 6144, .cts = 83500, + }, { + .tmds = 106500, .n = 6144, .cts = 106500, + }, { + .tmds = 108000, .n = 6144, .cts = 108000, + }, { + .tmds = 148352, .n = 5824, .cts = 140625, + }, { + .tmds = 148500, .n = 6144, .cts = 148500, + }, { + .tmds = 297000, .n = 5120, .cts = 247500, + } +}; + +struct hdmi_mpll_config { + u64 mpixelclock; + /* Mode of Operation and PLL Dividers Control Register */ + u32 cpce; + /* PLL Gmp Control Register */ + u32 gmp; + /* PLL Current COntrol Register */ + u32 curr; +}; + +struct hdmi_phy_config { + u64 mpixelclock; + u32 sym_ctr; /* clock symbol and transmitter control */ + u32 term; /* transmission termination value */ + u32 vlev_ctr; /* voltage level control */ +}; + +static const struct hdmi_phy_config rockchip_phy_config[] = { + { + .mpixelclock = 74250, + .sym_ctr = 0x8009, .term = 0x0004, .vlev_ctr = 0x0272, + }, { + .mpixelclock = 148500, + .sym_ctr = 0x802b, .term = 0x0004, .vlev_ctr = 0x028d, + }, { + .mpixelclock = 297000, + .sym_ctr = 0x8039, .term = 0x0005, .vlev_ctr = 0x028d, + }, { + .mpixelclock = ~0ul, + .sym_ctr = 0x0000, .term = 0x0000, .vlev_ctr = 0x0000, + } +}; + +static const struct hdmi_mpll_config rockchip_mpll_cfg[] = { + { + .mpixelclock = 40000, + .cpce = 0x00b3, .gmp = 0x0000, .curr = 0x0018, + }, { + .mpixelclock = 65000, + .cpce = 0x0072, .gmp = 0x0001, .curr = 0x0028, + }, { + .mpixelclock = 66000, + .cpce = 0x013e, .gmp = 0x0003, .curr = 0x0038, + }, { + .mpixelclock = 83500, + .cpce = 0x0072, .gmp = 0x0001, .curr = 0x0028, + }, { + .mpixelclock = 146250, + .cpce = 0x0051, .gmp = 0x0002, .curr = 0x0038, + }, { + .mpixelclock = 148500, + .cpce = 0x0051, .gmp = 0x0003, .curr = 0x0000, + }, { + .mpixelclock = ~0ul, + .cpce = 0x0051, .gmp = 0x0003, .curr = 0x0000, + } +}; + +static const u32 csc_coeff_default[3][4] = { + { 0x2000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x2000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x2000, 0x0000 } +}; + +static void hdmi_set_clock_regenerator(struct rk3288_hdmi *regs, u32 n, u32 cts) +{ + u8 cts3; + u8 n3; + + /* first set ncts_atomic_write (if present) */ + n3 = HDMI_AUD_N3_NCTS_ATOMIC_WRITE; + writel(n3, ®s->aud_n3); + + /* set cts_manual (if present) */ + cts3 = HDMI_AUD_CTS3_CTS_MANUAL; + + cts3 |= HDMI_AUD_CTS3_N_SHIFT_1 << HDMI_AUD_CTS3_N_SHIFT_OFFSET; + cts3 |= (cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK; + + /* write cts values; cts3 must be written first */ + writel(cts3, ®s->aud_cts3); + writel((cts >> 8) & 0xff, ®s->aud_cts2); + writel(cts & 0xff, ®s->aud_cts1); + + /* write n values; n1 must be written last */ + n3 |= (n >> 16) & HDMI_AUD_N3_AUDN19_16_MASK; + writel(n3, ®s->aud_n3); + writel((n >> 8) & 0xff, ®s->aud_n2); + writel(n & 0xff, ®s->aud_n1); + + writel(HDMI_AUD_INPUTCLKFS_128, ®s->aud_inputclkfs); +} + +static int hdmi_lookup_n_cts(u32 pixel_clk) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(n_cts_table); i++) + if (pixel_clk <= n_cts_table[i].tmds) + break; + + if (i >= ARRAY_SIZE(n_cts_table)) + return -1; + + return i; +} + +static void hdmi_audio_set_samplerate(struct rk3288_hdmi *regs, u32 pixel_clk) +{ + u32 clk_n, clk_cts; + int index; + + index = hdmi_lookup_n_cts(pixel_clk); + if (index == -1) { + debug("audio not supported for pixel clk %d\n", pixel_clk); + return; + } + + clk_n = n_cts_table[index].n; + clk_cts = n_cts_table[index].cts; + hdmi_set_clock_regenerator(regs, clk_n, clk_cts); +} + +/* + * this submodule is responsible for the video data synchronization. + * for example, for rgb 4:4:4 input, the data map is defined as + * pin{47~40} <==> r[7:0] + * pin{31~24} <==> g[7:0] + * pin{15~8} <==> b[7:0] + */ +static void hdmi_video_sample(struct rk3288_hdmi *regs) +{ + u32 color_format = 0x01; + u8 val; + + val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE | + ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) & + HDMI_TX_INVID0_VIDEO_MAPPING_MASK); + + writel(val, ®s->tx_invid0); + + /* enable tx stuffing: when de is inactive, fix the output data to 0 */ + val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE | + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE | + HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE; + writel(val, ®s->tx_instuffing); + writel(0x0, ®s->tx_gydata0); + writel(0x0, ®s->tx_gydata1); + writel(0x0, ®s->tx_rcrdata0); + writel(0x0, ®s->tx_rcrdata1); + writel(0x0, ®s->tx_bcbdata0); + writel(0x0, ®s->tx_bcbdata1); +} + +static void hdmi_update_csc_coeffs(struct rk3288_hdmi *regs) +{ + u32 i, j; + u32 csc_scale = 1; + + /* the csc registers are sequential, alternating msb then lsb */ + for (i = 0; i < ARRAY_SIZE(csc_coeff_default); i++) { + for (j = 0; j < ARRAY_SIZE(csc_coeff_default[0]); j++) { + u32 coeff = csc_coeff_default[i][j]; + writel(coeff >> 8, ®s->csc_coef[i][j].msb); + writel(coeff && 0xff, ®s->csc_coef[i][j].lsb); + } + } + + clrsetbits_le32(®s->csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK, + csc_scale); +} + +static void hdmi_video_csc(struct rk3288_hdmi *regs) +{ + u32 color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP; + u32 interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; + + /* configure the csc registers */ + writel(interpolation, ®s->csc_cfg); + clrsetbits_le32(®s->csc_scale, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK, color_depth); + + hdmi_update_csc_coeffs(regs); +} + +static void hdmi_video_packetize(struct rk3288_hdmi *regs) +{ + u32 output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; + u32 remap_size = HDMI_VP_REMAP_YCC422_16BIT; + u32 color_depth = 0; + u8 val, vp_conf; + + /* set the packetizer registers */ + val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & + HDMI_VP_PR_CD_COLOR_DEPTH_MASK) | + ((0 << HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) & + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK); + writel(val, ®s->vp_pr_cd); + + clrsetbits_le32(®s->vp_stuff, HDMI_VP_STUFF_PR_STUFFING_MASK, + HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE); + + /* data from pixel repeater block */ + vp_conf = HDMI_VP_CONF_PR_EN_DISABLE | + HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; + + clrsetbits_le32(®s->vp_conf, HDMI_VP_CONF_PR_EN_MASK | + HDMI_VP_CONF_BYPASS_SELECT_MASK, vp_conf); + + clrsetbits_le32(®s->vp_stuff, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, + 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET); + + writel(remap_size, ®s->vp_remap); + + vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE | + HDMI_VP_CONF_PP_EN_DISABLE | + HDMI_VP_CONF_YCC422_EN_DISABLE; + + clrsetbits_le32(®s->vp_conf, HDMI_VP_CONF_BYPASS_EN_MASK | + HDMI_VP_CONF_PP_EN_ENMASK | HDMI_VP_CONF_YCC422_EN_MASK, + vp_conf); + + clrsetbits_le32(®s->vp_stuff, HDMI_VP_STUFF_PP_STUFFING_MASK | + HDMI_VP_STUFF_YCC422_STUFFING_MASK, + HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | + HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE); + + clrsetbits_le32(®s->vp_conf, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK, + output_select); +} + +static inline void hdmi_phy_test_clear(struct rk3288_hdmi *regs, u8 bit) +{ + clrsetbits_le32(®s->phy_tst0, HDMI_PHY_TST0_TSTCLR_MASK, + bit << HDMI_PHY_TST0_TSTCLR_OFFSET); +} + +static int hdmi_phy_wait_i2c_done(struct rk3288_hdmi *regs, u32 msec) +{ + ulong start; + u32 val; + + start = get_timer(0); + do { + val = readl(®s->ih_i2cmphy_stat0); + if (val & 0x3) { + writel(val, ®s->ih_i2cmphy_stat0); + return 0; + } + + udelay(100); + } while (get_timer(start) < msec); + + return 1; +} + +static void hdmi_phy_i2c_write(struct rk3288_hdmi *regs, uint data, uint addr) +{ + writel(0xff, ®s->ih_i2cmphy_stat0); + writel(addr, ®s->phy_i2cm_address_addr); + writel((u8)(data >> 8), ®s->phy_i2cm_datao_1_addr); + writel((u8)(data >> 0), ®s->phy_i2cm_datao_0_addr); + writel(HDMI_PHY_I2CM_OPERATION_ADDR_WRITE, + ®s->phy_i2cm_operation_addr); + + hdmi_phy_wait_i2c_done(regs, 1000); +} + +static void hdmi_phy_enable_power(struct rk3288_hdmi *regs, uint enable) +{ + clrsetbits_le32(®s->phy_conf0, HDMI_PHY_CONF0_PDZ_MASK, + enable << HDMI_PHY_CONF0_PDZ_OFFSET); +} + +static void hdmi_phy_enable_tmds(struct rk3288_hdmi *regs, uint enable) +{ + clrsetbits_le32(®s->phy_conf0, HDMI_PHY_CONF0_ENTMDS_MASK, + enable << HDMI_PHY_CONF0_ENTMDS_OFFSET); +} + +static void hdmi_phy_enable_spare(struct rk3288_hdmi *regs, uint enable) +{ + clrsetbits_le32(®s->phy_conf0, HDMI_PHY_CONF0_SPARECTRL_MASK, + enable << HDMI_PHY_CONF0_SPARECTRL_OFFSET); +} + +static void hdmi_phy_gen2_pddq(struct rk3288_hdmi *regs, uint enable) +{ + clrsetbits_le32(®s->phy_conf0, HDMI_PHY_CONF0_GEN2_PDDQ_MASK, + enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET); +} + +static void hdmi_phy_gen2_txpwron(struct rk3288_hdmi *regs, uint enable) +{ + clrsetbits_le32(®s->phy_conf0, + HDMI_PHY_CONF0_GEN2_TXPWRON_MASK, + enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET); +} + +static void hdmi_phy_sel_data_en_pol(struct rk3288_hdmi *regs, uint enable) +{ + clrsetbits_le32(®s->phy_conf0, + HDMI_PHY_CONF0_SELDATAENPOL_MASK, + enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET); +} + +static void hdmi_phy_sel_interface_control(struct rk3288_hdmi *regs, + uint enable) +{ + clrsetbits_le32(®s->phy_conf0, HDMI_PHY_CONF0_SELDIPIF_MASK, + enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET); +} + +static int hdmi_phy_configure(struct rk3288_hdmi *regs, u32 mpixelclock) +{ + ulong start; + u8 i, val; + + writel(HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS, + ®s->mc_flowctrl); + + /* gen2 tx power off */ + hdmi_phy_gen2_txpwron(regs, 0); + + /* gen2 pddq */ + hdmi_phy_gen2_pddq(regs, 1); + + /* phy reset */ + writel(HDMI_MC_PHYRSTZ_DEASSERT, ®s->mc_phyrstz); + writel(HDMI_MC_PHYRSTZ_ASSERT, ®s->mc_phyrstz); + writel(HDMI_MC_HEACPHY_RST_ASSERT, ®s->mc_heacphy_rst); + + hdmi_phy_test_clear(regs, 1); + writel(HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2, ®s->phy_i2cm_slave_addr); + hdmi_phy_test_clear(regs, 0); + + /* pll/mpll cfg - always match on final entry */ + for (i = 0; rockchip_mpll_cfg[i].mpixelclock != (~0ul); i++) + if (mpixelclock <= rockchip_mpll_cfg[i].mpixelclock) + break; + + hdmi_phy_i2c_write(regs, rockchip_mpll_cfg[i].cpce, PHY_OPMODE_PLLCFG); + hdmi_phy_i2c_write(regs, rockchip_mpll_cfg[i].gmp, PHY_PLLGMPCTRL); + hdmi_phy_i2c_write(regs, rockchip_mpll_cfg[i].curr, PHY_PLLCURRCTRL); + + hdmi_phy_i2c_write(regs, 0x0000, PHY_PLLPHBYCTRL); + hdmi_phy_i2c_write(regs, 0x0006, PHY_PLLCLKBISTPHASE); + + for (i = 0; rockchip_phy_config[i].mpixelclock != (~0ul); i++) + if (mpixelclock <= rockchip_phy_config[i].mpixelclock) + break; + + /* + * resistance term 133ohm cfg + * preemp cgf 0.00 + * tx/ck lvl 10 + */ + hdmi_phy_i2c_write(regs, rockchip_phy_config[i].term, PHY_TXTERM); + hdmi_phy_i2c_write(regs, rockchip_phy_config[i].sym_ctr, + PHY_CKSYMTXCTRL); + hdmi_phy_i2c_write(regs, rockchip_phy_config[i].vlev_ctr, PHY_VLEVCTRL); + + /* remove clk term */ + hdmi_phy_i2c_write(regs, 0x8000, PHY_CKCALCTRL); + + hdmi_phy_enable_power(regs, 1); + + /* toggle tmds enable */ + hdmi_phy_enable_tmds(regs, 0); + hdmi_phy_enable_tmds(regs, 1); + + /* gen2 tx power on */ + hdmi_phy_gen2_txpwron(regs, 1); + hdmi_phy_gen2_pddq(regs, 0); + + hdmi_phy_enable_spare(regs, 1); + + /* wait for phy pll lock */ + start = get_timer(0); + do { + val = readl(®s->phy_stat0); + if (!(val & HDMI_PHY_TX_PHY_LOCK)) + return 0; + + udelay(100); + } while (get_timer(start) < 5); + + return -1; +} + +static int hdmi_phy_init(struct rk3288_hdmi *regs, uint mpixelclock) +{ + int i, ret; + + /* hdmi phy spec says to do the phy initialization sequence twice */ + for (i = 0; i < 2; i++) { + hdmi_phy_sel_data_en_pol(regs, 1); + hdmi_phy_sel_interface_control(regs, 0); + hdmi_phy_enable_tmds(regs, 0); + hdmi_phy_enable_power(regs, 0); + + /* enable csc */ + ret = hdmi_phy_configure(regs, mpixelclock); + if (ret) { + debug("hdmi phy config failure %d\n", ret); + return ret; + } + } + + return 0; +} + +static void hdmi_av_composer(struct rk3288_hdmi *regs, + const struct display_timing *edid) +{ + u8 mdataenablepolarity = 1; + u8 inv_val; + uint hbl; + uint vbl; + + hbl = edid->hback_porch.typ + edid->hfront_porch.typ + + edid->hsync_len.typ; + vbl = edid->vback_porch.typ + edid->vfront_porch.typ + + edid->vsync_len.typ; + + /* set up hdmi_fc_invidconf */ + inv_val = HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE; + + inv_val |= (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH ? + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW); + + inv_val |= (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH ? + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW); + + inv_val |= (mdataenablepolarity ? + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW); + + /* + * TODO(sjg@chromium.org>: Need to check for HDMI / DVI + * inv_val |= (edid->hdmi_monitor_detected ? + * HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE : + * HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE); + */ + inv_val |= HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE; + + inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW; + + inv_val |= HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE; + + writel(inv_val, ®s->fc_invidconf); + + /* set up horizontal active pixel width */ + writel(edid->hactive.typ >> 8, ®s->fc_inhactv1); + writel(edid->hactive.typ, ®s->fc_inhactv0); + + /* set up vertical active lines */ + writel(edid->vactive.typ >> 8, ®s->fc_invactv1); + writel(edid->vactive.typ, ®s->fc_invactv0); + + /* set up horizontal blanking pixel region width */ + writel(hbl >> 8, ®s->fc_inhblank1); + writel(hbl, ®s->fc_inhblank0); + + /* set up vertical blanking pixel region width */ + writel(vbl, ®s->fc_invblank); + + /* set up hsync active edge delay width (in pixel clks) */ + writel(edid->hfront_porch.typ >> 8, ®s->fc_hsyncindelay1); + writel(edid->hfront_porch.typ, ®s->fc_hsyncindelay0); + + /* set up vsync active edge delay (in lines) */ + writel(edid->vfront_porch.typ, ®s->fc_vsyncindelay); + + /* set up hsync active pulse width (in pixel clks) */ + writel(edid->hsync_len.typ >> 8, ®s->fc_hsyncinwidth1); + writel(edid->hsync_len.typ, ®s->fc_hsyncinwidth0); + + /* set up vsync active edge delay (in lines) */ + writel(edid->vsync_len.typ, ®s->fc_vsyncinwidth); +} + +/* hdmi initialization step b.4 */ +static void hdmi_enable_video_path(struct rk3288_hdmi *regs) +{ + u8 clkdis; + + /* control period minimum duration */ + writel(12, ®s->fc_ctrldur); + writel(32, ®s->fc_exctrldur); + writel(1, ®s->fc_exctrlspac); + + /* set to fill tmds data channels */ + writel(0x0b, ®s->fc_ch0pream); + writel(0x16, ®s->fc_ch1pream); + writel(0x21, ®s->fc_ch2pream); + + /* enable pixel clock and tmds data path */ + clkdis = 0x7f; + clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; + writel(clkdis, ®s->mc_clkdis); + + clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; + writel(clkdis, ®s->mc_clkdis); + + clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; + writel(clkdis, ®s->mc_clkdis); +} + +/* workaround to clear the overflow condition */ +static void hdmi_clear_overflow(struct rk3288_hdmi *regs) +{ + u8 val, count; + + /* tmds software reset */ + writel((u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, ®s->mc_swrstz); + + val = readl(®s->fc_invidconf); + + for (count = 0; count < 4; count++) + writel(val, ®s->fc_invidconf); +} + +static void hdmi_audio_set_format(struct rk3288_hdmi *regs) +{ + writel(HDMI_AUD_CONF0_I2S_SELECT | HDMI_AUD_CONF0_I2S_IN_EN_0, + ®s->aud_conf0); + + + writel(HDMI_AUD_CONF1_I2S_MODE_STANDARD_MODE | + HDMI_AUD_CONF1_I2S_WIDTH_16BIT, ®s->aud_conf1); + + writel(0x00, ®s->aud_conf2); +} + +static void hdmi_audio_fifo_reset(struct rk3288_hdmi *regs) +{ + writel((u8)~HDMI_MC_SWRSTZ_II2SSWRST_REQ, ®s->mc_swrstz); + writel(HDMI_AUD_CONF0_SW_AUDIO_FIFO_RST, ®s->aud_conf0); + + writel(0x00, ®s->aud_int); + writel(0x00, ®s->aud_int1); +} + +static void hdmi_init_interrupt(struct rk3288_hdmi *regs) +{ + u8 ih_mute; + + /* + * boot up defaults are: + * hdmi_ih_mute = 0x03 (disabled) + * hdmi_ih_mute_* = 0x00 (enabled) + * + * disable top level interrupt bits in hdmi block + */ + ih_mute = readl(®s->ih_mute) | + HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT | + HDMI_IH_MUTE_MUTE_ALL_INTERRUPT; + + writel(ih_mute, ®s->ih_mute); + + /* enable i2c master done irq */ + writel(~0x04, ®s->i2cm_int); + + /* enable i2c client nack % arbitration error irq */ + writel(~0x44, ®s->i2cm_ctlint); + + /* enable phy i2cm done irq */ + writel(HDMI_PHY_I2CM_INT_ADDR_DONE_POL, ®s->phy_i2cm_int_addr); + + /* enable phy i2cm nack & arbitration error irq */ + writel(HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, + ®s->phy_i2cm_ctlint_addr); + + /* enable cable hot plug irq */ + writel((u8)~HDMI_PHY_HPD, ®s->phy_mask0); + + /* clear hotplug interrupts */ + writel(HDMI_IH_PHY_STAT0_HPD, ®s->ih_phy_stat0); +} + +static u8 hdmi_get_plug_in_status(struct rk3288_hdmi *regs) +{ + u8 val = readl(®s->phy_stat0) & HDMI_PHY_HPD; + + return !!(val); +} + +static int hdmi_wait_for_hpd(struct rk3288_hdmi *regs) +{ + ulong start; + + start = get_timer(0); + do { + if (hdmi_get_plug_in_status(regs)) + return 0; + udelay(100); + } while (get_timer(start) < 30000); + + return -1; +} + +static int hdmi_ddc_wait_i2c_done(struct rk3288_hdmi *regs, int msec) +{ + u32 val; + ulong start; + + start = get_timer(0); + do { + val = readl(®s->ih_i2cm_stat0); + if (val & 0x2) { + writel(val, ®s->ih_i2cm_stat0); + return 0; + } + + udelay(100); + } while (get_timer(start) < msec); + + return 1; +} + +static void hdmi_ddc_reset(struct rk3288_hdmi *regs) +{ + clrbits_le32(®s->i2cm_softrstz, HDMI_I2CM_SOFTRSTZ); +} + +static int hdmi_read_edid(struct rk3288_hdmi *regs, int block, u8 *buff) +{ + int shift = (block % 2) * 0x80; + int edid_read_err = 0; + u32 trytime = 5; + u32 n, j, val; + + /* set ddc i2c clk which devided from ddc_clk to 100khz */ + writel(0x7a, ®s->i2cm_ss_scl_hcnt_0_addr); + writel(0x8d, ®s->i2cm_ss_scl_lcnt_0_addr); + + /* + * TODO(sjg@chromium.org): The above values don't work - these ones + * work better, but generate lots of errors in the data. + */ + writel(0x0d, ®s->i2cm_ss_scl_hcnt_0_addr); + writel(0x0d, ®s->i2cm_ss_scl_lcnt_0_addr); + clrsetbits_le32(®s->i2cm_div, HDMI_I2CM_DIV_FAST_STD_MODE, + HDMI_I2CM_DIV_STD_MODE); + + writel(HDMI_I2CM_SLAVE_DDC_ADDR, ®s->i2cm_slave); + writel(HDMI_I2CM_SEGADDR_DDC, ®s->i2cm_segaddr); + writel(block >> 1, ®s->i2cm_segptr); + + while (trytime--) { + edid_read_err = 0; + + for (n = 0; n < HDMI_EDID_BLOCK_SIZE / 8; n++) { + writel(shift + 8 * n, ®s->i2c_address); + + if (block == 0) + clrsetbits_le32(®s->i2cm_operation, + HDMI_I2CM_OPT_RD8, + HDMI_I2CM_OPT_RD8); + else + clrsetbits_le32(®s->i2cm_operation, + HDMI_I2CM_OPT_RD8_EXT, + HDMI_I2CM_OPT_RD8_EXT); + + if (hdmi_ddc_wait_i2c_done(regs, 10)) { + hdmi_ddc_reset(regs); + edid_read_err = 1; + break; + } + + for (j = 0; j < 8; j++) { + val = readl(®s->i2cm_buf0 + j); + buff[8 * n + j] = val; + } + } + + if (!edid_read_err) + break; + } + + return edid_read_err; +} + +static u8 pre_buf[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x04, 0x69, 0xfa, 0x23, 0xc8, 0x28, 0x01, 0x00, + 0x10, 0x17, 0x01, 0x03, 0x80, 0x33, 0x1d, 0x78, + 0x2a, 0xd9, 0x45, 0xa2, 0x55, 0x4d, 0xa0, 0x27, + 0x12, 0x50, 0x54, 0xb7, 0xef, 0x00, 0x71, 0x4f, + 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, 0xb3, 0x00, + 0xd1, 0xc0, 0x81, 0xc0, 0x81, 0x00, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x44, 0x34, 0x4c, + 0x4d, 0x54, 0x46, 0x30, 0x37, 0x35, 0x39, 0x37, + 0x36, 0x0a, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x4b, 0x18, 0x53, 0x11, 0x00, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, + 0x00, 0x41, 0x53, 0x55, 0x53, 0x20, 0x56, 0x53, + 0x32, 0x33, 0x38, 0x0a, 0x20, 0x20, 0x01, 0xb0, + 0x02, 0x03, 0x22, 0x71, 0x4f, 0x01, 0x02, 0x03, + 0x11, 0x12, 0x13, 0x04, 0x14, 0x05, 0x0e, 0x0f, + 0x1d, 0x1e, 0x1f, 0x10, 0x23, 0x09, 0x17, 0x07, + 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00, + 0x10, 0x00, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, + 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xfd, 0x1e, + 0x11, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x00, 0x72, + 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, + 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1e, 0x01, 0x1d, + 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20, 0xb8, 0x28, + 0x55, 0x40, 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1e, + 0x8c, 0x0a, 0xd0, 0x90, 0x20, 0x40, 0x31, 0x20, + 0x0c, 0x40, 0x55, 0x00, 0xfd, 0x1e, 0x11, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, +}; + +static int rk_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + struct rk_hdmi_priv *priv = dev_get_priv(dev); + u32 edid_size = HDMI_EDID_BLOCK_SIZE; + int ret; + + if (0) { + edid_size = sizeof(pre_buf); + memcpy(buf, pre_buf, edid_size); + } else { + ret = hdmi_read_edid(priv->regs, 0, buf); + if (ret) { + debug("failed to read edid.\n"); + return -1; + } + + if (buf[0x7e] != 0) { + hdmi_read_edid(priv->regs, 1, + buf + HDMI_EDID_BLOCK_SIZE); + edid_size += HDMI_EDID_BLOCK_SIZE; + } + } + + return edid_size; +} + +static int rk_hdmi_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *edid) +{ + struct rk_hdmi_priv *priv = dev_get_priv(dev); + struct rk3288_hdmi *regs = priv->regs; + int ret; + + debug("hdmi, mode info : clock %d hdis %d vdis %d\n", + edid->pixelclock.typ, edid->hactive.typ, edid->vactive.typ); + + hdmi_av_composer(regs, edid); + + ret = hdmi_phy_init(regs, edid->pixelclock.typ); + if (ret) + return ret; + + hdmi_enable_video_path(regs); + + hdmi_audio_fifo_reset(regs); + hdmi_audio_set_format(regs); + hdmi_audio_set_samplerate(regs, edid->pixelclock.typ); + + hdmi_video_packetize(regs); + hdmi_video_csc(regs); + hdmi_video_sample(regs); + + hdmi_clear_overflow(regs); + + return 0; +} + +static int rk_hdmi_ofdata_to_platdata(struct udevice *dev) +{ + struct rk_hdmi_priv *priv = dev_get_priv(dev); + + priv->regs = (struct rk3288_hdmi *)dev_get_addr(dev); + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + + return 0; +} + +static int rk_hdmi_probe(struct udevice *dev) +{ + struct display_plat *uc_plat = dev_get_uclass_platdata(dev); + struct rk_hdmi_priv *priv = dev_get_priv(dev); + struct udevice *reg, *clk; + int periph; + int ret; + int vop_id = uc_plat->source_id; + + ret = clk_get_by_index(dev, 0, &clk, &periph); + if (!ret) + ret = clk_set_periph_rate(clk, periph, 0); + if (ret) { + debug("%s: Failed to set EDP clock: ret=%d\n", __func__, ret); + return ret; + } + + /* + * Configure the maximum clock to permit whatever resolution the + * monitor wants + */ + ret = clk_get_by_index(uc_plat->src_dev, 0, &clk, &periph); + if (!ret) + ret = clk_set_periph_rate(clk, periph, 384000000); + if (ret < 0) { + debug("%s: Failed to set clock in source device '%s': ret=%d\n", + __func__, uc_plat->src_dev->name, ret); + return ret; + } + + ret = regulator_get_by_platname("vcc50_hdmi", ®); + if (!ret) + ret = regulator_set_enable(reg, true); + if (ret) + debug("%s: Cannot set regulator vcc50_hdmi\n", __func__); + + /* hdmi source select hdmi controller */ + rk_setreg(&priv->grf->soc_con6, 1 << 15); + + /* hdmi data from vop id */ + rk_setreg(&priv->grf->soc_con6, (vop_id == 1) ? (1 << 4) : (1 << 4)); + + ret = hdmi_wait_for_hpd(priv->regs); + if (ret < 0) { + debug("hdmi can not get hpd signal\n"); + return -1; + } + + hdmi_init_interrupt(priv->regs); + + return 0; +} + +static const struct dm_display_ops rk_hdmi_ops = { + .read_edid = rk_hdmi_read_edid, + .enable = rk_hdmi_enable, +}; + +static const struct udevice_id rk_hdmi_ids[] = { + { .compatible = "rockchip,rk3288-dw-hdmi" }, + { } +}; + +U_BOOT_DRIVER(hdmi_rockchip) = { + .name = "hdmi_rockchip", + .id = UCLASS_DISPLAY, + .of_match = rk_hdmi_ids, + .ops = &rk_hdmi_ops, + .ofdata_to_platdata = rk_hdmi_ofdata_to_platdata, + .probe = rk_hdmi_probe, + .priv_auto_alloc_size = sizeof(struct rk_hdmi_priv), +};

Some Rockchip SoCs support embedded DisplayPort output. Add a display driver for this so that these displays can be used on supported boards.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/include/asm/arch-rockchip/edp_rk3288.h | 636 +++++++++++++ drivers/video/rockchip/Makefile | 2 +- drivers/video/rockchip/rk_edp.c | 1081 +++++++++++++++++++++++ 3 files changed, 1718 insertions(+), 1 deletion(-) create mode 100644 arch/arm/include/asm/arch-rockchip/edp_rk3288.h create mode 100644 drivers/video/rockchip/rk_edp.c
diff --git a/arch/arm/include/asm/arch-rockchip/edp_rk3288.h b/arch/arm/include/asm/arch-rockchip/edp_rk3288.h new file mode 100644 index 0000000..a9e2761 --- /dev/null +++ b/arch/arm/include/asm/arch-rockchip/edp_rk3288.h @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2015 Google, Inc + * Copyright 2014 Rockchip Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ASM_ARCH_EDP_H +#define _ASM_ARCH_EDP_H + +struct rk3288_edp { + u8 res0[0x10]; + u32 dp_tx_version; + u8 res1[0x4]; + u32 func_en_1; + u32 func_en_2; + u32 video_ctl_1; + u32 video_ctl_2; + u32 video_ctl_3; + u32 video_ctl_4; + u8 res2[0xc]; + u32 video_ctl_8; + u8 res3[0x4]; + u32 video_ctl_10; + u32 total_line_l; + u32 total_line_h; + u32 active_line_l; + u32 active_line_h; + u32 v_f_porch; + u32 vsync; + u32 v_b_porch; + u32 total_pixel_l; + u32 total_pixel_h; + u32 active_pixel_l; + u32 active_pixel_h; + u32 h_f_porch_l; + u32 h_f_porch_h; + u32 hsync_l; + u32 hysnc_h; + u32 h_b_porch_l; + u32 h_b_porch_h; + u32 vid_status; + u32 total_line_sta_l; + u32 total_line_sta_h; + u32 active_line_sta_l; + u32 active_line_sta_h; + u32 v_f_porch_sta; + u32 vsync_sta; + u32 v_b_porch_sta; + u32 total_pixel_sta_l; + u32 total_pixel_sta_h; + u32 active_pixel_sta_l; + u32 active_pixel_sta_h; + u32 h_f_porch_sta_l; + u32 h_f_porch_sta_h; + u32 hsync_sta_l; + u32 hsync_sta_h; + u32 h_b_porch_sta_l; + u32 h_b_porch__sta_h; + u8 res4[0x28]; + u32 pll_reg_1; + u8 res5[4]; + u32 ssc_reg; + u8 res6[0xc]; + u32 tx_common; + u32 tx_common2; + u8 res7[0x4]; + u32 dp_aux; + u32 dp_bias; + u32 dp_test; + u32 dp_pd; + u32 dp_reserv1; + u32 dp_reserv2; + u8 res8[0x224]; + u32 lane_map; + u8 res9[0x14]; + u32 analog_ctl_2; + u8 res10[0x48]; + u32 int_state; + u32 common_int_sta_1; + u32 common_int_sta_2; + u32 common_int_sta_3; + u32 common_int_sta_4; + u32 spdif_biphase_int_sta; + u8 res11[0x4]; + u32 dp_int_sta; + u32 common_int_mask_1; + u32 common_int_mask_2; + u32 common_int_mask_3; + u32 common_int_mask_4; + u8 res12[0x08]; + u32 int_sta_mask; + u32 int_ctl; + u8 res13[0x200]; + u32 sys_ctl_1; + u32 sys_ctl_2; + u32 sys_ctl_3; + u32 sys_ctl_4; + u32 dp_vid_ctl; + u8 res14[0x4]; + u32 dp_aud_ctl; + u8 res15[0x24]; + u32 pkt_send_ctl; + u8 res16[0x4]; + u32 dp_hdcp_ctl; + u8 res17[0x34]; + u32 link_bw_set; + u32 lane_count_set; + u32 dp_training_ptn_set; + u32 ln_link_trn_ctl[4]; + u8 res18[0x4]; + u32 dp_hw_link_training; + u8 res19[0x1c]; + u32 dp_debug_ctl; + u32 hpd_deglitch_l; + u32 hpd_deglitch_h; + u8 res20[0x14]; + u32 dp_link_debug_ctl; + u8 res21[0x1c]; + u32 m_vid_0; + u32 m_vid_1; + u32 m_vid_2; + u32 n_vid_0; + u32 n_vid_1; + u32 n_vid_2; + u32 m_vid_mon; + u8 res22[0x14]; + u32 dp_video_fifo_thrd; + u8 res23[0x8]; + u32 dp_audio_margin; + u8 res24[0x20]; + u32 dp_m_cal_ctl; + u32 m_vid_gen_filter_th; + u8 res25[0x10]; + u32 m_aud_gen_filter_th; + u8 res26[0x4]; + u32 aux_ch_sta; + u32 aux_err_num; + u32 aux_ch_defer_dtl; + u32 aux_rx_comm; + u32 buf_data_ctl; + u32 aux_ch_ctl_1; + u32 aux_addr_7_0; + u32 aux_addr_15_8; + u32 aux_addr_19_16; + u32 aux_ch_ctl_2; + u8 res27[0x18]; + u32 buf_data[16]; + u32 soc_general_ctl; + u8 res29[0x1e0]; + u32 pll_reg_2; + u32 pll_reg_3; + u32 pll_reg_4; + u8 res30[0x10]; + u32 pll_reg_5; +}; +check_member(rk3288_edp, pll_reg_5, 0xa00); + +/* func_en_1 */ +#define VID_CAP_FUNC_EN_N (0x1 << 6) +#define VID_FIFO_FUNC_EN_N (0x1 << 5) +#define AUD_FIFO_FUNC_EN_N (0x1 << 4) +#define AUD_FUNC_EN_N (0x1 << 3) +#define HDCP_FUNC_EN_N (0x1 << 2) +#define SW_FUNC_EN_N (0x1 << 0) + +/* func_en_2 */ +#define SSC_FUNC_EN_N (0x1 << 7) +#define AUX_FUNC_EN_N (0x1 << 2) +#define SERDES_FIFO_FUNC_EN_N (0x1 << 1) +#define LS_CLK_DOMAIN_FUNC_EN_N (0x1 << 0) + +/* video_ctl_1 */ +#define VIDEO_EN (0x1 << 7) +#define VIDEO_MUTE (0x1 << 6) + +/* video_ctl_2 */ +#define IN_D_RANGE_MASK (0x1 << 7) +#define IN_D_RANGE_SHIFT (7) +#define IN_D_RANGE_CEA (0x1 << 7) +#define IN_D_RANGE_VESA (0x0 << 7) +#define IN_BPC_MASK (0x7 << 4) +#define IN_BPC_SHIFT (4) +#define IN_BPC_12_BITS (0x3 << 4) +#define IN_BPC_10_BITS (0x2 << 4) +#define IN_BPC_8_BITS (0x1 << 4) +#define IN_BPC_6_BITS (0x0 << 4) +#define IN_COLOR_F_MASK (0x3 << 0) +#define IN_COLOR_F_SHIFT (0) +#define IN_COLOR_F_YCBCR444 (0x2 << 0) +#define IN_COLOR_F_YCBCR422 (0x1 << 0) +#define IN_COLOR_F_RGB (0x0 << 0) + +/* video_ctl_3 */ +#define IN_YC_COEFFI_MASK (0x1 << 7) +#define IN_YC_COEFFI_SHIFT (7) +#define IN_YC_COEFFI_ITU709 (0x1 << 7) +#define IN_YC_COEFFI_ITU601 (0x0 << 7) +#define VID_CHK_UPDATE_TYPE_MASK (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_SHIFT (4) +#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) + +/* video_ctl_4 */ +#define BIST_EN (0x1 << 3) +#define BIST_WH_64 (0x1 << 2) +#define BIST_WH_32 (0x0 << 2) +#define BIST_TYPE_COLR_BAR (0x0 << 0) +#define BIST_TYPE_GRAY_BAR (0x1 << 0) +#define BIST_TYPE_MOBILE_BAR (0x2 << 0) + +/* video_ctl_8 */ +#define VID_HRES_TH(x) (((x) & 0xf) << 4) +#define VID_VRES_TH(x) (((x) & 0xf) << 0) + +/* video_ctl_10 */ +#define F_SEL (0x1 << 4) +#define INTERACE_SCAN_CFG (0x1 << 2) +#define INTERACD_SCAN_CFG_OFFSET 2 +#define VSYNC_POLARITY_CFG (0x1 << 1) +#define VSYNC_POLARITY_CFG_OFFSET 1 +#define HSYNC_POLARITY_CFG (0x1 << 0) +#define HSYNC_POLARITY_CFG_OFFSET 0 + +/* dp_pd */ +#define PD_INC_BG (0x1 << 7) +#define PD_EXP_BG (0x1 << 6) +#define PD_AUX (0x1 << 5) +#define PD_PLL (0x1 << 4) +#define PD_CH3 (0x1 << 3) +#define PD_CH2 (0x1 << 2) +#define PD_CH1 (0x1 << 1) +#define PD_CH0 (0x1 << 0) + +/* pll_reg_1 */ +#define REF_CLK_24M (0x1 << 1) +#define REF_CLK_27M (0x0 << 1) + +/* line_map */ +#define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6) +#define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6) +#define LANE3_MAP_LOGIC_LANE_2 (0x2 << 6) +#define LANE3_MAP_LOGIC_LANE_3 (0x3 << 6) +#define LANE2_MAP_LOGIC_LANE_0 (0x0 << 4) +#define LANE2_MAP_LOGIC_LANE_1 (0x1 << 4) +#define LANE2_MAP_LOGIC_LANE_2 (0x2 << 4) +#define LANE2_MAP_LOGIC_LANE_3 (0x3 << 4) +#define LANE1_MAP_LOGIC_LANE_0 (0x0 << 2) +#define LANE1_MAP_LOGIC_LANE_1 (0x1 << 2) +#define LANE1_MAP_LOGIC_LANE_2 (0x2 << 2) +#define LANE1_MAP_LOGIC_LANE_3 (0x3 << 2) +#define LANE0_MAP_LOGIC_LANE_0 (0x0 << 0) +#define LANE0_MAP_LOGIC_LANE_1 (0x1 << 0) +#define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) +#define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) + +/* analog_ctl_2 */ +#define SEL_24M (0x1 << 3) + +/* common_int_sta_1 */ +#define VSYNC_DET (0x1 << 7) +#define PLL_LOCK_CHG (0x1 << 6) +#define SPDIF_ERR (0x1 << 5) +#define SPDIF_UNSTBL (0x1 << 4) +#define VID_FORMAT_CHG (0x1 << 3) +#define AUD_CLK_CHG (0x1 << 2) +#define VID_CLK_CHG (0x1 << 1) +#define SW_INT (0x1 << 0) + +/* common_int_sta_2 */ +#define ENC_EN_CHG (0x1 << 6) +#define HW_BKSV_RDY (0x1 << 3) +#define HW_SHA_DONE (0x1 << 2) +#define HW_AUTH_STATE_CHG (0x1 << 1) +#define HW_AUTH_DONE (0x1 << 0) + +/* common_int_sta_3 */ +#define AFIFO_UNDER (0x1 << 7) +#define AFIFO_OVER (0x1 << 6) +#define R0_CHK_FLAG (0x1 << 5) + +/* common_int_sta_4 */ +#define PSR_ACTIVE (0x1 << 7) +#define PSR_INACTIVE (0x1 << 6) +#define SPDIF_BI_PHASE_ERR (0x1 << 5) +#define HOTPLUG_CHG (0x1 << 2) +#define HPD_LOST (0x1 << 1) +#define PLUG (0x1 << 0) + +/* dp_int_sta */ +#define INT_HPD (0x1 << 6) +#define HW_LT_DONE (0x1 << 5) +#define SINK_LOST (0x1 << 3) +#define LINK_LOST (0x1 << 2) +#define RPLY_RECEIV (0x1 << 1) +#define AUX_ERR (0x1 << 0) + +/* int_ctl */ +#define SOFT_INT_CTRL (0x1 << 2) +#define INT_POL (0x1 << 0) + +/* sys_ctl_1 */ +#define DET_STA (0x1 << 2) +#define FORCE_DET (0x1 << 1) +#define DET_CTRL (0x1 << 0) + +/* sys_ctl_2 */ +#define CHA_CRI(x) (((x) & 0xf) << 4) +#define CHA_STA (0x1 << 2) +#define FORCE_CHA (0x1 << 1) +#define CHA_CTRL (0x1 << 0) + +/* sys_ctl_3 */ +#define HPD_STATUS (0x1 << 6) +#define F_HPD (0x1 << 5) +#define HPD_CTRL (0x1 << 4) +#define HDCP_RDY (0x1 << 3) +#define STRM_VALID (0x1 << 2) +#define F_VALID (0x1 << 1) +#define VALID_CTRL (0x1 << 0) + +/* sys_ctl_4 */ +#define FIX_M_AUD (0x1 << 4) +#define ENHANCED (0x1 << 3) +#define FIX_M_VID (0x1 << 2) +#define M_VID_UPDATE_CTRL (0x3 << 0) + +/* pll_reg_2 */ +#define LDO_OUTPUT_V_SEL_145 (2 << 6) +#define KVCO_DEFALUT (1 << 4) +#define CHG_PUMP_CUR_SEL_5US (1 << 2) +#define V2L_CUR_SEL_1MA (1 << 0) + +/* pll_reg_3 */ +#define LOCK_DET_CNT_SEL_256 (2 << 5) +#define LOOP_FILTER_RESET (0 << 4) +#define PALL_SSC_RESET (0 << 3) +#define LOCK_DET_BYPASS (0 << 2) +#define PLL_LOCK_DET_MODE (0 << 1) +#define PLL_LOCK_DET_FORCE (0 << 0) + +/* pll_reg_5 */ +#define REGULATOR_V_SEL_950MV (2 << 4) +#define STANDBY_CUR_SEL (0 << 3) +#define CHG_PUMP_INOUT_CTRL_1200MV (1 << 1) +#define CHG_PUMP_INPUT_CTRL_OP (0 << 0) + +/* ssc_reg */ +#define SSC_OFFSET (0 << 6) +#define SSC_MODE (1 << 4) +#define SSC_DEPTH (9 << 0) + +/* tx_common */ +#define TX_SWING_PRE_EMP_MODE (1 << 7) +#define PRE_DRIVER_PW_CTRL1 (0 << 5) +#define LP_MODE_CLK_REGULATOR (0 << 4) +#define RESISTOR_MSB_CTRL (0 << 3) +#define RESISTOR_CTRL (7 << 0) + +/* dp_aux */ +#define DP_AUX_COMMON_MODE (0 << 4) +#define DP_AUX_EN (0 << 3) +#define AUX_TERM_50OHM (3 << 0) + +/* dp_bias */ +#define DP_BG_OUT_SEL (4 << 4) +#define DP_DB_CUR_CTRL (0 << 3) +#define DP_BG_SEL (1 << 2) +#define DP_RESISTOR_TUNE_BG (2 << 0) + +/* dp_reserv2 */ +#define CH1_CH3_SWING_EMP_CTRL (5 << 4) +#define CH0_CH2_SWING_EMP_CTRL (5 << 0) + +/* dp_training_ptn_set */ +#define SCRAMBLING_DISABLE (0x1 << 5) +#define SCRAMBLING_ENABLE (0x0 << 5) +#define LINK_QUAL_PATTERN_SET_MASK (0x7 << 2) +#define LINK_QUAL_PATTERN_SET_HBR2 (0x5 << 2) +#define LINK_QUAL_PATTERN_SET_80BIT (0x4 << 2) +#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2) +#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2) +#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2) +#define SW_TRAINING_PATTERN_SET_MASK (0x3 << 0) +#define SW_TRAINING_PATTERN_SET_PTN2 (0x2 << 0) +#define SW_TRAINING_PATTERN_SET_PTN1 (0x1 << 0) +#define SW_TRAINING_PATTERN_SET_DISABLE (0x0 << 0) + +/* dp_hw_link_training_ctl */ +#define HW_LT_ERR_CODE_MASK 0x70 +#define HW_LT_ERR_CODE_SHIFT 4 +#define HW_LT_EN (0x1 << 0) + +/* dp_debug_ctl */ +#define PLL_LOCK (0x1 << 4) +#define F_PLL_LOCK (0x1 << 3) +#define PLL_LOCK_CTRL (0x1 << 2) +#define POLL_EN (0x1 << 1) +#define PN_INV (0x1 << 0) + +/* aux_ch_sta */ +#define AUX_BUSY (0x1 << 4) +#define AUX_STATUS_MASK (0xf << 0) + +/* aux_ch_defer_ctl */ +#define DEFER_CTRL_EN (0x1 << 7) +#define DEFER_COUNT(x) (((x) & 0x7f) << 0) + +/* aux_rx_comm */ +#define AUX_RX_COMM_I2C_DEFER (0x2 << 2) +#define AUX_RX_COMM_AUX_DEFER (0x2 << 0) + +/* buffer_data_ctl */ +#define BUF_CLR (0x1 << 7) +#define BUF_HAVE_DATA (0x1 << 4) +#define BUF_DATA_COUNT(x) (((x) & 0xf) << 0) + +/* aux_ch_ctl_1 */ +#define AUX_LENGTH(x) (((x - 1) & 0xf) << 4) +#define AUX_TX_COMM_MASK (0xf << 0) +#define AUX_TX_COMM_DP_TRANSACTION (0x1 << 3) +#define AUX_TX_COMM_I2C_TRANSACTION (0x0 << 3) +#define AUX_TX_COMM_MOT (0x1 << 2) +#define AUX_TX_COMM_WRITE (0x0 << 0) +#define AUX_TX_COMM_READ (0x1 << 0) + +/* aux_ch_ctl_2 */ +#define PD_AUX_IDLE (0x1 << 3) +#define ADDR_ONLY (0x1 << 1) +#define AUX_EN (0x1 << 0) + +/* tx_sw_reset */ +#define RST_DP_TX (0x1 << 0) + +/* analog_ctl_1 */ +#define TX_TERMINAL_CTRL_50_OHM (0x1 << 4) + +/* analog_ctl_3 */ +#define DRIVE_DVDD_BIT_1_0625V (0x4 << 5) +#define VCO_BIT_600_MICRO (0x5 << 0) + +/* pll_filter_ctl_1 */ +#define PD_RING_OSC (0x1 << 6) +#define AUX_TERMINAL_CTRL_37_5_OHM (0x0 << 4) +#define AUX_TERMINAL_CTRL_45_OHM (0x1 << 4) +#define AUX_TERMINAL_CTRL_50_OHM (0x2 << 4) +#define AUX_TERMINAL_CTRL_65_OHM (0x3 << 4) +#define TX_CUR1_2X (0x1 << 2) +#define TX_CUR_16_MA (0x3 << 0) + +/* Definition for DPCD Register */ +#define DPCD_DPCD_REV (0x0000) +#define DPCD_MAX_LINK_RATE (0x0001) +#define DPCD_MAX_LANE_COUNT (0x0002) +#define DP_MAX_LANE_COUNT_MASK 0x1f +#define DP_TPS3_SUPPORTED (1 << 6) +#define DP_ENHANCED_FRAME_CAP (1 << 7) + +#define DPCD_LINK_BW_SET (0x0100) +#define DPCD_LANE_COUNT_SET (0x0101) + +#define DPCD_TRAINING_PATTERN_SET (0x0102) +#define DP_TRAINING_PATTERN_DISABLE 0 +#define DP_TRAINING_PATTERN_1 1 +#define DP_TRAINING_PATTERN_2 2 +#define DP_TRAINING_PATTERN_3 3 +#define DP_TRAINING_PATTERN_MASK 0x3 + +#define DPCD_TRAINING_LANE0_SET (0x0103) +#define DP_TRAIN_VOLTAGE_SWING_MASK 0x3 +#define DP_TRAIN_VOLTAGE_SWING_SHIFT 0 +#define DP_TRAIN_MAX_SWING_REACHED (1 << 2) +#define DP_TRAIN_VOLTAGE_SWING_400 (0 << 0) +#define DP_TRAIN_VOLTAGE_SWING_600 (1 << 0) +#define DP_TRAIN_VOLTAGE_SWING_800 (2 << 0) +#define DP_TRAIN_VOLTAGE_SWING_1200 (3 << 0) + +#define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3) +#define DP_TRAIN_PRE_EMPHASIS_0 (0 << 3) +#define DP_TRAIN_PRE_EMPHASIS_3_5 (1 << 3) +#define DP_TRAIN_PRE_EMPHASIS_6 (2 << 3) +#define DP_TRAIN_PRE_EMPHASIS_9_5 (3 << 3) + +#define DP_TRAIN_PRE_EMPHASIS_SHIFT 3 +#define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5) + +#define DPCD_LANE0_1_STATUS (0x0202) +#define DPCD_LANE2_3_STATUS (0x0203) +#define DP_LANE_CR_DONE (1 << 0) +#define DP_LANE_CHANNEL_EQ_DONE (1 << 1) +#define DP_LANE_SYMBOL_LOCKED (1 << 2) +#define DP_CHANNEL_EQ_BITS (DP_LANE_CR_DONE |\ + DP_LANE_CHANNEL_EQ_DONE |\ + DP_LANE_SYMBOL_LOCKED) + +#define DPCD_LANE_ALIGN_STATUS_UPDATED (0x0204) +#define DP_INTERLANE_ALIGN_DONE (1 << 0) +#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) +#define DP_LINK_STATUS_UPDATED (1 << 7) + +#define DPCD_ADJUST_REQUEST_LANE0_1 (0x0206) +#define DPCD_ADJUST_REQUEST_LANE2_3 (0x0207) +#define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK 0x03 +#define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0 +#define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK 0x0c +#define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT 2 +#define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK 0x30 +#define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4 +#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0 +#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6 + +#define DPCD_TEST_REQUEST (0x0218) +#define DPCD_TEST_RESPONSE (0x0260) +#define DPCD_TEST_EDID_CHECKSUM (0x0261) +#define DPCD_LINK_POWER_STATE (0x0600) +#define DP_SET_POWER_D0 0x1 +#define DP_SET_POWER_D3 0x2 +#define DP_SET_POWER_MASK 0x3 + +#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff) +#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff) +#define AUX_ADDR_19_16(x) (((x) >> 16) & 0x0f) + +#define STREAM_ON_TIMEOUT 100 +#define PLL_LOCK_TIMEOUT 10 +#define DP_INIT_TRIES 10 + +#define EDID_ADDR 0x50 +#define EDID_LENGTH 0x80 +#define EDID_HEADER 0x00 +#define EDID_EXTENSION_FLAG 0x7e + + +enum dpcd_request { + DPCD_READ, + DPCD_WRITE, +}; + +enum dp_irq_type { + DP_IRQ_TYPE_HP_CABLE_IN, + DP_IRQ_TYPE_HP_CABLE_OUT, + DP_IRQ_TYPE_HP_CHANGE, + DP_IRQ_TYPE_UNKNOWN, +}; + +enum color_coefficient { + COLOR_YCBCR601, + COLOR_YCBCR709 +}; + +enum dynamic_range { + VESA, + CEA +}; + +enum clock_recovery_m_value_type { + CALCULATED_M, + REGISTER_M +}; + +enum video_timing_recognition_type { + VIDEO_TIMING_FROM_CAPTURE, + VIDEO_TIMING_FROM_REGISTER +}; + +enum pattern_set { + PRBS7, + D10_2, + TRAINING_PTN1, + TRAINING_PTN2, + DP_NONE +}; + +enum color_space { + CS_RGB, + CS_YCBCR422, + CS_YCBCR444 +}; + +enum color_depth { + COLOR_6, + COLOR_8, + COLOR_10, + COLOR_12 +}; + +enum link_rate_type { + LINK_RATE_1_62GBPS = 0x06, + LINK_RATE_2_70GBPS = 0x0a +}; + +enum link_lane_count_type { + LANE_CNT1 = 1, + LANE_CNT2 = 2, + LANE_CNT4 = 4 +}; + +enum link_training_state { + LT_START, + LT_CLK_RECOVERY, + LT_EQ_TRAINING, + FINISHED, + FAILED +}; + +enum voltage_swing_level { + VOLTAGE_LEVEL_0, + VOLTAGE_LEVEL_1, + VOLTAGE_LEVEL_2, + VOLTAGE_LEVEL_3, +}; + +enum pre_emphasis_level { + PRE_EMPHASIS_LEVEL_0, + PRE_EMPHASIS_LEVEL_1, + PRE_EMPHASIS_LEVEL_2, + PRE_EMPHASIS_LEVEL_3, +}; + +enum analog_power_block { + AUX_BLOCK, + CH0_BLOCK, + CH1_BLOCK, + CH2_BLOCK, + CH3_BLOCK, + ANALOG_TOTAL, + POWER_ALL +}; + +struct link_train { + unsigned char revision; + u8 link_rate; + u8 lane_count; +}; + +#endif diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index 361fcb5..5566719 100644 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -5,4 +5,4 @@ # SPDX-License-Identifier: GPL-2.0+ #
-obj-y += rk_hdmi.o +obj-y += rk_edp.o rk_hdmi.o diff --git a/drivers/video/rockchip/rk_edp.c b/drivers/video/rockchip/rk_edp.c new file mode 100644 index 0000000..0ed4467 --- /dev/null +++ b/drivers/video/rockchip/rk_edp.c @@ -0,0 +1,1081 @@ +/* + * Copyright (c) 2015 Google, Inc + * Copyright 2014 Rockchip Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <display.h> +#include <dm.h> +#include <edid.h> +#include <panel.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/edp_rk3288.h> +#include <asm/arch/grf_rk3288.h> +#include <dt-bindings/clock/rk3288-cru.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define MAX_CR_LOOP 5 +#define MAX_EQ_LOOP 5 +#define DP_LINK_STATUS_SIZE 6 + +static const char * const voltage_names[] = { + "0.4V", "0.6V", "0.8V", "1.2V" +}; +static const char * const pre_emph_names[] = { + "0dB", "3.5dB", "6dB", "9.5dB" +}; + +#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 +#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5 + +struct rk_edp_priv { + struct rk3288_edp *regs; + struct rk3288_grf *grf; + struct udevice *panel; + struct link_train link_train; + u8 train_set[4]; +}; + +static void rk_edp_init_refclk(struct rk3288_edp *regs) +{ + writel(SEL_24M, ®s->analog_ctl_2); + writel(REF_CLK_24M, ®s->pll_reg_1); + + writel(LDO_OUTPUT_V_SEL_145 | KVCO_DEFALUT | CHG_PUMP_CUR_SEL_5US | + V2L_CUR_SEL_1MA, ®s->pll_reg_2); + + writel(LOCK_DET_CNT_SEL_256 | LOOP_FILTER_RESET | PALL_SSC_RESET | + LOCK_DET_BYPASS | PLL_LOCK_DET_MODE | PLL_LOCK_DET_FORCE, + ®s->pll_reg_3); + + writel(REGULATOR_V_SEL_950MV | STANDBY_CUR_SEL | + CHG_PUMP_INOUT_CTRL_1200MV | CHG_PUMP_INPUT_CTRL_OP, + ®s->pll_reg_5); + + writel(SSC_OFFSET | SSC_MODE | SSC_DEPTH, ®s->ssc_reg); + + writel(TX_SWING_PRE_EMP_MODE | PRE_DRIVER_PW_CTRL1 | + LP_MODE_CLK_REGULATOR | RESISTOR_MSB_CTRL | RESISTOR_CTRL, + ®s->tx_common); + + writel(DP_AUX_COMMON_MODE | DP_AUX_EN | AUX_TERM_50OHM, + ®s->dp_aux); + + writel(DP_BG_OUT_SEL | DP_DB_CUR_CTRL | DP_BG_SEL | DP_RESISTOR_TUNE_BG, + ®s->dp_bias); + + writel(CH1_CH3_SWING_EMP_CTRL | CH0_CH2_SWING_EMP_CTRL, + ®s->dp_reserv2); +} + +static void rk_edp_init_interrupt(struct rk3288_edp *regs) +{ + /* Set interrupt pin assertion polarity as high */ + writel(INT_POL, ®s->int_ctl); + + /* Clear pending registers */ + writel(0xff, ®s->common_int_sta_1); + writel(0x4f, ®s->common_int_sta_2); + writel(0xff, ®s->common_int_sta_3); + writel(0x27, ®s->common_int_sta_4); + writel(0x7f, ®s->dp_int_sta); + + /* 0:mask,1: unmask */ + writel(0x00, ®s->common_int_mask_1); + writel(0x00, ®s->common_int_mask_2); + writel(0x00, ®s->common_int_mask_3); + writel(0x00, ®s->common_int_mask_4); + writel(0x00, ®s->int_sta_mask); +} + +static void rk_edp_enable_sw_function(struct rk3288_edp *regs) +{ + clrbits_le32(®s->func_en_1, SW_FUNC_EN_N); +} + +static bool rk_edp_get_pll_locked(struct rk3288_edp *regs) +{ + u32 val; + + val = readl(®s->dp_debug_ctl); + + return val & PLL_LOCK; +} + +static int rk_edp_init_analog_func(struct rk3288_edp *regs) +{ + ulong start; + + writel(0x00, ®s->dp_pd); + writel(PLL_LOCK_CHG, ®s->common_int_sta_1); + + clrbits_le32(®s->dp_debug_ctl, F_PLL_LOCK | PLL_LOCK_CTRL); + + start = get_timer(0); + while (!rk_edp_get_pll_locked(regs)) { + if (get_timer(start) > PLL_LOCK_TIMEOUT) { + printf("%s: PLL is not locked\n", __func__); + return -ETIMEDOUT; + } + } + + /* Enable Serdes FIFO function and Link symbol clock domain module */ + clrbits_le32(®s->func_en_2, SERDES_FIFO_FUNC_EN_N | + LS_CLK_DOMAIN_FUNC_EN_N | AUX_FUNC_EN_N | + SSC_FUNC_EN_N); + + return 0; +} + +static void rk_edp_init_aux(struct rk3288_edp *regs) +{ + /* Clear inerrupts related to AUX channel */ + writel(AUX_FUNC_EN_N, ®s->dp_int_sta); + + /* Disable AUX channel module */ + setbits_le32(®s->func_en_2, AUX_FUNC_EN_N); + + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ + writel(DEFER_CTRL_EN | DEFER_COUNT(1), ®s->aux_ch_defer_dtl); + + /* Enable AUX channel module */ + clrbits_le32(®s->func_en_2, AUX_FUNC_EN_N); +} + +static int rk_edp_aux_enable(struct rk3288_edp *regs) +{ + ulong start; + + setbits_le32(®s->aux_ch_ctl_2, AUX_EN); + start = get_timer(0); + do { + if (!(readl(®s->aux_ch_ctl_2) & AUX_EN)) + return 0; + } while (get_timer(start) < 20); + + return -ETIMEDOUT; +} + +static int rk_edp_is_aux_reply(struct rk3288_edp *regs) +{ + ulong start; + + start = get_timer(0); + while (!(readl(®s->dp_int_sta) & RPLY_RECEIV)) { + if (get_timer(start) > 10) + return -ETIMEDOUT; + } + + writel(RPLY_RECEIV, ®s->dp_int_sta); + + return 0; +} + +static int rk_edp_start_aux_transaction(struct rk3288_edp *regs) +{ + int val, ret; + + /* Enable AUX CH operation */ + ret = rk_edp_aux_enable(regs); + if (ret) { + debug("AUX CH enable timeout!\n"); + return ret; + } + + /* Is AUX CH command reply received? */ + if (rk_edp_is_aux_reply(regs)) { + debug("AUX CH command reply failed!\n"); + return ret; + } + + /* Clear interrupt source for AUX CH access error */ + val = readl(®s->dp_int_sta); + if (val & AUX_ERR) { + writel(AUX_ERR, ®s->dp_int_sta); + return -EIO; + } + + /* Check AUX CH error access status */ + val = readl(®s->dp_int_sta); + if (val & AUX_STATUS_MASK) { + debug("AUX CH error happens: %d\n\n", val & AUX_STATUS_MASK); + return -EIO; + } + + return 0; +} + +static int rk_edp_dpcd_transfer(struct rk3288_edp *regs, + unsigned int val_addr, u8 *in_data, + unsigned int length, + enum dpcd_request request) +{ + int val; + int i, try_times; + u8 *data; + int ret = 0; + u32 len = 0; + + while (length) { + len = min(length, 16U); + for (try_times = 0; try_times < 10; try_times++) { + data = in_data; + /* Clear AUX CH data buffer */ + writel(BUF_CLR, ®s->buf_data_ctl); + + /* Select DPCD device address */ + writel(AUX_ADDR_7_0(val_addr), ®s->aux_addr_7_0); + writel(AUX_ADDR_15_8(val_addr), ®s->aux_addr_15_8); + writel(AUX_ADDR_19_16(val_addr), ®s->aux_addr_19_16); + + /* + * Set DisplayPort transaction and read 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + if (request == DPCD_WRITE) { + val = AUX_LENGTH(len) | + AUX_TX_COMM_DP_TRANSACTION | + AUX_TX_COMM_WRITE; + for (i = 0; i < len; i++) + writel(*data++, ®s->buf_data[i]); + } else + val = AUX_LENGTH(len) | + AUX_TX_COMM_DP_TRANSACTION | + AUX_TX_COMM_READ; + + writel(val, ®s->aux_ch_ctl_1); + + /* Start AUX transaction */ + ret = rk_edp_start_aux_transaction(regs); + if (ret == 0) + break; + else + printf("read dpcd Aux Transaction fail!\n"); + } + + if (ret) + return ret; + + if (request == DPCD_READ) { + for (i = 0; i < len; i++) + *data++ = (u8)readl(®s->buf_data[i]); + } + + length -= len; + val_addr += len; + in_data += len; + } + + return 0; +} + +static int rk_edp_dpcd_read(struct rk3288_edp *regs, u32 addr, u8 *values, + size_t size) +{ + return rk_edp_dpcd_transfer(regs, addr, values, size, DPCD_READ); +} + +static int rk_edp_dpcd_write(struct rk3288_edp *regs, u32 addr, u8 *values, + size_t size) +{ + return rk_edp_dpcd_transfer(regs, addr, values, size, DPCD_WRITE); +} + + +static int rk_edp_link_power_up(struct rk_edp_priv *edp) +{ + u8 value; + int ret; + + /* DP_SET_POWER register is only available on DPCD v1.1 and later */ + if (edp->link_train.revision < 0x11) + return 0; + + ret = rk_edp_dpcd_read(edp->regs, DPCD_LINK_POWER_STATE, &value, 1); + if (ret) + return ret; + + value &= ~DP_SET_POWER_MASK; + value |= DP_SET_POWER_D0; + + ret = rk_edp_dpcd_write(edp->regs, DPCD_LINK_POWER_STATE, &value, 1); + if (ret) + return ret; + + /* + * According to the DP 1.1 specification, a "Sink Device must exit the + * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink + * Control Field" (register 0x600). + */ + mdelay(1); + + return 0; +} + +static int rk_edp_link_configure(struct rk_edp_priv *edp) +{ + u8 values[2]; + + values[0] = edp->link_train.link_rate; + values[1] = edp->link_train.lane_count; + + return rk_edp_dpcd_write(edp->regs, DPCD_LINK_BW_SET, values, + sizeof(values)); +} + +static void rk_edp_set_link_training(struct rk_edp_priv *edp, + const u8 *training_values) +{ + int i; + + for (i = 0; i < edp->link_train.lane_count; i++) + writel(training_values[i], &edp->regs->ln_link_trn_ctl[i]); +} + +static u8 edp_link_status(const u8 *link_status, int r) +{ + return link_status[r - DPCD_LANE0_1_STATUS]; +} + +static int rk_edp_dpcd_read_link_status(struct rk_edp_priv *edp, + u8 *link_status) +{ + return rk_edp_dpcd_read(edp->regs, DPCD_LANE0_1_STATUS, link_status, + DP_LINK_STATUS_SIZE); +} + +static u8 edp_get_lane_status(const u8 *link_status, int lane) +{ + int i = DPCD_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + u8 l = edp_link_status(link_status, i); + + return (l >> s) & 0xf; +} + +static int rk_edp_clock_recovery(const u8 *link_status, int lane_count) +{ + int lane; + u8 lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = edp_get_lane_status(link_status, lane); + if ((lane_status & DP_LANE_CR_DONE) == 0) + return -EIO; + } + + return 0; +} + +static int rk_edp_channel_eq(const u8 *link_status, int lane_count) +{ + u8 lane_align; + u8 lane_status; + int lane; + + lane_align = edp_link_status(link_status, + DPCD_LANE_ALIGN_STATUS_UPDATED); + if (!(lane_align & DP_INTERLANE_ALIGN_DONE)) + return -EIO; + for (lane = 0; lane < lane_count; lane++) { + lane_status = edp_get_lane_status(link_status, lane); + if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) + return -EIO; + } + + return 0; +} + +static uint rk_edp_get_adjust_request_voltage(const u8 *link_status, int lane) +{ + int i = DPCD_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : + DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); + u8 l = edp_link_status(link_status, i); + + return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + +static uint rk_edp_get_adjust_request_pre_emphasis(const u8 *link_status, + int lane) +{ + int i = DPCD_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : + DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); + u8 l = edp_link_status(link_status, i); + + return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + +static void edp_get_adjust_train(const u8 *link_status, int lane_count, + u8 train_set[]) +{ + uint v = 0; + uint p = 0; + int lane; + + for (lane = 0; lane < lane_count; lane++) { + uint this_v, this_p; + + this_v = rk_edp_get_adjust_request_voltage(link_status, lane); + this_p = rk_edp_get_adjust_request_pre_emphasis(link_status, + lane); + + debug("requested signal parameters: lane %d voltage %s pre_emph %s\n", + lane, + voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT], + pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); + + if (this_v > v) + v = this_v; + if (this_p > p) + p = this_p; + } + + if (v >= DP_VOLTAGE_MAX) + v |= DP_TRAIN_MAX_SWING_REACHED; + + if (p >= DP_PRE_EMPHASIS_MAX) + p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + debug("using signal parameters: voltage %s pre_emph %s\n", + voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) + >> DP_TRAIN_VOLTAGE_SWING_SHIFT], + pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) + >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); + + for (lane = 0; lane < 4; lane++) + train_set[lane] = v | p; +} + +static int rk_edp_link_train_cr(struct rk_edp_priv *edp) +{ + struct rk3288_edp *regs = edp->regs; + int clock_recovery; + uint voltage, tries = 0; + u8 status[DP_LINK_STATUS_SIZE]; + int i, ret; + u8 value; + + value = DP_TRAINING_PATTERN_1; + writel(value, ®s->dp_training_ptn_set); + ret = rk_edp_dpcd_write(regs, DPCD_TRAINING_PATTERN_SET, &value, 1); + if (ret) + return ret; + memset(edp->train_set, '\0', sizeof(edp->train_set)); + + /* clock recovery loop */ + clock_recovery = 0; + tries = 0; + voltage = 0xff; + + while (1) { + rk_edp_set_link_training(edp, edp->train_set); + ret = rk_edp_dpcd_write(regs, DPCD_TRAINING_LANE0_SET, + edp->train_set, + edp->link_train.lane_count); + if (ret) + return ret; + + mdelay(1); + + ret = rk_edp_dpcd_read_link_status(edp, status); + if (ret) { + printf("displayport link status failed, ret=%d\n", ret); + break; + } + + clock_recovery = rk_edp_clock_recovery(status, + edp->link_train.lane_count); + if (!clock_recovery) + break; + + for (i = 0; i < edp->link_train.lane_count; i++) { + if ((edp->train_set[i] & + DP_TRAIN_MAX_SWING_REACHED) == 0) + break; + } + if (i == edp->link_train.lane_count) { + printf("clock recovery reached max voltage\n"); + break; + } + + if ((edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == + voltage) { + if (++tries == MAX_CR_LOOP) { + printf("clock recovery tried 5 times\n"); + break; + } + } else { + tries = 0; + } + + voltage = edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + + /* Compute new train_set as requested by sink */ + edp_get_adjust_train(status, edp->link_train.lane_count, + edp->train_set); + } + if (clock_recovery) { + printf("clock recovery failed: %d\n", clock_recovery); + return clock_recovery; + } else { + debug("clock recovery at voltage %d pre-emphasis %d\n", + edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, + (edp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT); + return 0; + } +} + +static int rk_edp_link_train_ce(struct rk_edp_priv *edp) +{ + struct rk3288_edp *regs = edp->regs; + int channel_eq; + u8 value; + int tries; + u8 status[DP_LINK_STATUS_SIZE]; + int ret; + + value = DP_TRAINING_PATTERN_2; + writel(value, ®s->dp_training_ptn_set); + ret = rk_edp_dpcd_write(regs, DPCD_TRAINING_PATTERN_SET, &value, 1); + if (ret) + return ret; + + /* channel equalization loop */ + channel_eq = 0; + for (tries = 0; tries < 5; tries++) { + rk_edp_set_link_training(edp, edp->train_set); + udelay(400); + + if (rk_edp_dpcd_read_link_status(edp, status) < 0) { + printf("displayport link status failed\n"); + return -1; + } + + channel_eq = rk_edp_channel_eq(status, + edp->link_train.lane_count); + if (!channel_eq) + break; + edp_get_adjust_train(status, edp->link_train.lane_count, + edp->train_set); + } + + if (channel_eq) { + printf("channel eq failed, ret=%d\n", channel_eq); + return channel_eq; + } + + debug("channel eq at voltage %d pre-emphasis %d\n", + edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, + (edp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) + >> DP_TRAIN_PRE_EMPHASIS_SHIFT); + + return 0; +} + +static int rk_edp_init_training(struct rk_edp_priv *edp) +{ + u8 values[3]; + int ret; + + ret = rk_edp_dpcd_read(edp->regs, DPCD_DPCD_REV, values, + sizeof(values)); + if (ret < 0) + return ret; + + edp->link_train.revision = values[0]; + edp->link_train.link_rate = values[1]; + edp->link_train.lane_count = values[2] & DP_MAX_LANE_COUNT_MASK; + + debug("max link rate:%d.%dGps max number of lanes:%d\n", + edp->link_train.link_rate * 27 / 100, + edp->link_train.link_rate * 27 % 100, + edp->link_train.lane_count); + + if ((edp->link_train.link_rate != LINK_RATE_1_62GBPS) && + (edp->link_train.link_rate != LINK_RATE_2_70GBPS)) { + debug("Rx Max Link Rate is abnormal :%x\n", + edp->link_train.link_rate); + return -EPERM; + } + + if (edp->link_train.lane_count == 0) { + debug("Rx Max Lane count is abnormal :%x\n", + edp->link_train.lane_count); + return -EPERM; + } + + ret = rk_edp_link_power_up(edp); + if (ret) + return ret; + + return rk_edp_link_configure(edp); +} + +static int rk_edp_hw_link_training(struct rk_edp_priv *edp) +{ + ulong start; + u32 val; + int ret; + + /* Set link rate and count as you want to establish */ + writel(edp->link_train.link_rate, &edp->regs->link_bw_set); + writel(edp->link_train.lane_count, &edp->regs->lane_count_set); + + ret = rk_edp_link_train_cr(edp); + if (ret) + return ret; + ret = rk_edp_link_train_ce(edp); + if (ret) + return ret; + + writel(HW_LT_EN, &edp->regs->dp_hw_link_training); + start = get_timer(0); + do { + val = readl(&edp->regs->dp_hw_link_training); + if (!(val & HW_LT_EN)) + break; + } while (get_timer(start) < 10); + + if (val & HW_LT_ERR_CODE_MASK) { + printf("edp hw link training error: %d\n", + val >> HW_LT_ERR_CODE_SHIFT); + return -EIO; + } + + return 0; +} + +static int rk_edp_select_i2c_device(struct rk3288_edp *regs, + unsigned int device_addr, + unsigned int val_addr) +{ + int ret; + + /* Set EDID device address */ + writel(device_addr, ®s->aux_addr_7_0); + writel(0x0, ®s->aux_addr_15_8); + writel(0x0, ®s->aux_addr_19_16); + + /* Set offset from base address of EDID device */ + writel(val_addr, ®s->buf_data[0]); + + /* + * Set I2C transaction and write address + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + writel(AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | + AUX_TX_COMM_WRITE, ®s->aux_ch_ctl_1); + + /* Start AUX transaction */ + ret = rk_edp_start_aux_transaction(regs); + if (ret != 0) { + debug("select_i2c_device Aux Transaction fail!\n"); + return ret; + } + + return 0; +} + +static int rk_edp_i2c_read(struct rk3288_edp *regs, unsigned int device_addr, + unsigned int val_addr, unsigned int count, u8 edid[]) +{ + u32 val; + unsigned int i, j; + unsigned int cur_data_idx; + unsigned int defer = 0; + int ret = 0; + + for (i = 0; i < count; i += 16) { + for (j = 0; j < 10; j++) { /* try 10 times */ + /* Clear AUX CH data buffer */ + writel(BUF_CLR, ®s->buf_data_ctl); + + /* Set normal AUX CH command */ + clrbits_le32(®s->aux_ch_ctl_2, ADDR_ONLY); + + /* + * If Rx sends defer, Tx sends only reads + * request without sending addres + */ + if (!defer) { + ret = rk_edp_select_i2c_device(regs, + device_addr, + val_addr + i); + } else { + defer = 0; + } + + /* + * Set I2C transaction and write data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + writel(AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ, ®s->aux_ch_ctl_1); + + /* Start AUX transaction */ + ret = rk_edp_start_aux_transaction(regs); + if (ret == 0) { + break; + } else { + debug("Aux Transaction fail!\n"); + continue; + } + + /* Check if Rx sends defer */ + val = readl(®s->aux_rx_comm); + if (val == AUX_RX_COMM_AUX_DEFER || + val == AUX_RX_COMM_I2C_DEFER) { + debug("Defer: %d\n\n", val); + defer = 1; + } + } + + if (ret) + return ret; + + for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { + val = readl(®s->buf_data[cur_data_idx]); + edid[i + cur_data_idx] = (u8)val; + } + } + + return 0; +} + +static int rk_edp_set_link_train(struct rk_edp_priv *edp) +{ + int ret; + + ret = rk_edp_init_training(edp); + if (ret) { + printf("DP LT init failed!\n"); + return ret; + } + + ret = rk_edp_hw_link_training(edp); + if (ret) + return ret; + + return 0; +} + +static void rk_edp_init_video(struct rk3288_edp *regs) +{ + writel(VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG, + ®s->common_int_sta_1); + writel(CHA_CRI(4) | CHA_CTRL, ®s->sys_ctl_2); + writel(VID_HRES_TH(2) | VID_VRES_TH(0), ®s->video_ctl_8); +} + +static void rk_edp_config_video_slave_mode(struct rk3288_edp *regs) +{ + clrbits_le32(®s->func_en_1, VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N); +} + +static void rk_edp_set_video_cr_mn(struct rk3288_edp *regs, + enum clock_recovery_m_value_type type, + u32 m_value, + u32 n_value) +{ + if (type == REGISTER_M) { + setbits_le32(®s->sys_ctl_4, FIX_M_VID); + writel(m_value & 0xff, ®s->m_vid_0); + writel((m_value >> 8) & 0xff, ®s->m_vid_1); + writel((m_value >> 16) & 0xff, ®s->m_vid_2); + + writel(n_value & 0xf, ®s->n_vid_0); + writel((n_value >> 8) & 0xff, ®s->n_vid_1); + writel((n_value >> 16) & 0xff, ®s->n_vid_2); + } else { + clrbits_le32(®s->sys_ctl_4, FIX_M_VID); + + writel(0x00, ®s->n_vid_0); + writel(0x80, ®s->n_vid_1); + writel(0x00, ®s->n_vid_2); + } +} + +static int rk_edp_is_video_stream_clock_on(struct rk3288_edp *regs) +{ + ulong start; + u32 val; + + start = get_timer(0); + do { + val = readl(®s->sys_ctl_1); + + /* must write value to update DET_STA bit status */ + writel(val, ®s->sys_ctl_1); + val = readl(®s->sys_ctl_1); + if (!(val & DET_STA)) + continue; + + val = readl(®s->sys_ctl_2); + + /* must write value to update CHA_STA bit status */ + writel(val, ®s->sys_ctl_2); + val = readl(®s->sys_ctl_2); + if (!(val & CHA_STA)) + return 0; + + } while (get_timer(start) < 100); + + return -ETIMEDOUT; +} + +static int rk_edp_is_video_stream_on(struct rk_edp_priv *edp) +{ + ulong start; + u32 val; + + start = get_timer(0); + do { + val = readl(&edp->regs->sys_ctl_3); + + /* must write value to update STRM_VALID bit status */ + writel(val, &edp->regs->sys_ctl_3); + + val = readl(&edp->regs->sys_ctl_3); + if (!(val & STRM_VALID)) + return 0; + } while (get_timer(start) < 100); + + return -ETIMEDOUT; +} + +static int rk_edp_config_video(struct rk_edp_priv *edp) +{ + int ret; + + rk_edp_config_video_slave_mode(edp->regs); + + if (!rk_edp_get_pll_locked(edp->regs)) { + debug("PLL is not locked yet.\n"); + return -ETIMEDOUT; + } + + ret = rk_edp_is_video_stream_clock_on(edp->regs); + if (ret) + return ret; + + /* Set to use the register calculated M/N video */ + rk_edp_set_video_cr_mn(edp->regs, CALCULATED_M, 0, 0); + + /* For video bist, Video timing must be generated by register */ + clrbits_le32(&edp->regs->video_ctl_10, F_SEL); + + /* Disable video mute */ + clrbits_le32(&edp->regs->video_ctl_1, VIDEO_MUTE); + + /* Enable video at next frame */ + setbits_le32(&edp->regs->video_ctl_1, VIDEO_EN); + + return rk_edp_is_video_stream_on(edp); +} + +static void rockchip_edp_force_hpd(struct rk_edp_priv *edp) +{ + setbits_le32(&edp->regs->sys_ctl_3, F_HPD | HPD_CTRL); +} + +static int rockchip_edp_get_plug_in_status(struct rk_edp_priv *edp) +{ + u32 val; + + val = readl(&edp->regs->sys_ctl_3); + if (val & HPD_STATUS) + return 1; + + return 0; +} + +/* + * support edp HPD function + * some hardware version do not support edp hdp, + * we use 200ms to try to get the hpd single now, + * if we can not get edp hpd single, it will delay 200ms, + * also meet the edp power timing request, to compatible + * all of the hardware version + */ +static void rockchip_edp_wait_hpd(struct rk_edp_priv *edp) +{ + ulong start; + + start = get_timer(0); + do { + if (rockchip_edp_get_plug_in_status(edp)) + return; + udelay(100); + } while (get_timer(start) < 200); + + debug("do not get hpd single, force hpd\n"); + rockchip_edp_force_hpd(edp); +} + +static int rk_edp_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *edid) +{ + struct rk_edp_priv *priv = dev_get_priv(dev); + int ret = 0; + + ret = rk_edp_set_link_train(priv); + if (ret) { + printf("link train failed!\n"); + return ret; + } + + rk_edp_init_video(priv->regs); + ret = rk_edp_config_video(priv); + if (ret) { + printf("config video failed\n"); + return ret; + } + ret = panel_enable_backlight(priv->panel); + if (ret) { + debug("%s: backlight error: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int rk_edp_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + struct rk_edp_priv *priv = dev_get_priv(dev); + u32 edid_size = EDID_LENGTH; + int ret; + int i; + + for (i = 0; i < 3; i++) { + ret = rk_edp_i2c_read(priv->regs, EDID_ADDR, EDID_HEADER, + EDID_LENGTH, &buf[EDID_HEADER]); + if (ret) { + debug("EDID read failed\n"); + continue; + } + + /* + * check if the EDID has an extension flag, and read additional + * EDID data if needed + */ + if (buf[EDID_EXTENSION_FLAG]) { + edid_size += EDID_LENGTH; + ret = rk_edp_i2c_read(priv->regs, EDID_ADDR, + EDID_LENGTH, EDID_LENGTH, + &buf[EDID_LENGTH]); + if (ret) { + debug("EDID Read failed!\n"); + continue; + } + } + goto done; + } + + /* After 3 attempts, give up */ + return ret; + +done: + return edid_size; +} + +static int rk_edp_ofdata_to_platdata(struct udevice *dev) +{ + struct rk_edp_priv *priv = dev_get_priv(dev); + + priv->regs = (struct rk3288_edp *)dev_get_addr(dev); + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + + return 0; +} + +int rk_edp_probe(struct udevice *dev) +{ + struct display_plat *uc_plat = dev_get_uclass_platdata(dev); + struct rk_edp_priv *priv = dev_get_priv(dev); + struct rk3288_edp *regs = priv->regs; + struct udevice *clk; + int periph; + int ret; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "rockchip,panel", + &priv->panel); + if (ret) { + debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, + dev->name, ret); + return ret; + } + + int vop_id = uc_plat->source_id; + debug("%s, uc_plat=%p, vop_id=%u\n", __func__, uc_plat, vop_id); + + ret = clk_get_by_index(dev, 1, &clk, &periph); + if (!ret) + ret = clk_set_periph_rate(clk, periph, 0); + if (ret) { + debug("%s: Failed to set EDP clock: ret=%d\n", __func__, ret); + return ret; + } + + ret = clk_get_by_index(uc_plat->src_dev, 0, &clk, &periph); + if (!ret) + ret = clk_set_periph_rate(clk, periph, 192000000); + if (ret < 0) { + debug("%s: Failed to set clock in source device '%s': ret=%d\n", + __func__, uc_plat->src_dev->name, ret); + return ret; + } + + /* grf_edp_ref_clk_sel: from internal 24MHz or 27MHz clock */ + rk_setreg(&priv->grf->soc_con12, 1 << 4); + + /* select epd signal from vop0 or vop1 */ + rk_setreg(&priv->grf->soc_con6, (vop_id == 1) ? (1 << 5) : (1 << 5)); + + rockchip_edp_wait_hpd(priv); + + rk_edp_init_refclk(regs); + rk_edp_init_interrupt(regs); + rk_edp_enable_sw_function(regs); + ret = rk_edp_init_analog_func(regs); + if (ret) + return ret; + rk_edp_init_aux(regs); + + return 0; +} + +static const struct dm_display_ops dp_rockchip_ops = { + .read_edid = rk_edp_read_edid, + .enable = rk_edp_enable, +}; + +static const struct udevice_id rockchip_dp_ids[] = { + { .compatible = "rockchip,rk3288-edp" }, + { } +}; + +U_BOOT_DRIVER(dp_rockchip) = { + .name = "edp_rockchip", + .id = UCLASS_DISPLAY, + .of_match = rockchip_dp_ids, + .ops = &dp_rockchip_ops, + .ofdata_to_platdata = rk_edp_ofdata_to_platdata, + .probe = rk_edp_probe, + .priv_auto_alloc_size = sizeof(struct rk_edp_priv), +};

Some rockchip SoCs include video output (VOP). Add a driver to support this. It can output via a display driver (UCLASS_DISPLAY) and currently HDMI and eDP are supported.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/include/asm/arch-rockchip/vop_rk3288.h | 349 ++++++++++++++++++++++++ drivers/video/rockchip/Makefile | 2 +- drivers/video/rockchip/rk_vop.c | 346 +++++++++++++++++++++++ 3 files changed, 696 insertions(+), 1 deletion(-) create mode 100644 arch/arm/include/asm/arch-rockchip/vop_rk3288.h create mode 100644 drivers/video/rockchip/rk_vop.c
diff --git a/arch/arm/include/asm/arch-rockchip/vop_rk3288.h b/arch/arm/include/asm/arch-rockchip/vop_rk3288.h new file mode 100644 index 0000000..0104ba3 --- /dev/null +++ b/arch/arm/include/asm/arch-rockchip/vop_rk3288.h @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2015 Google, Inc + * Copyright 2014 Rockchip Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ASM_ARCH_VOP_RK3288_H +#define _ASM_ARCH_VOP_RK3288_H + +struct rk3288_vop { + u32 reg_cfg_done; + u32 version_info; + u32 sys_ctrl; + u32 sys_ctrl1; + u32 dsp_ctrl0; + u32 dsp_ctrl1; + u32 dsp_bg; + u32 mcu_ctrl; + u32 intr_ctrl0; + u32 intr_ctrl1; + u32 intr_reserved0; + u32 intr_reserved1; + + u32 win0_ctrl0; + u32 win0_ctrl1; + u32 win0_color_key; + u32 win0_vir; + u32 win0_yrgb_mst; + u32 win0_cbr_mst; + u32 win0_act_info; + u32 win0_dsp_info; + u32 win0_dsp_st; + u32 win0_scl_factor_yrgb; + u32 win0_scl_factor_cbr; + u32 win0_scl_offset; + u32 win0_src_alpha_ctrl; + u32 win0_dst_alpha_ctrl; + u32 win0_fading_ctrl; + u32 win0_reserved0; + + u32 win1_ctrl0; + u32 win1_ctrl1; + u32 win1_color_key; + u32 win1_vir; + u32 win1_yrgb_mst; + u32 win1_cbr_mst; + u32 win1_act_info; + u32 win1_dsp_info; + u32 win1_dsp_st; + u32 win1_scl_factor_yrgb; + u32 win1_scl_factor_cbr; + u32 win1_scl_offset; + u32 win1_src_alpha_ctrl; + u32 win1_dst_alpha_ctrl; + u32 win1_fading_ctrl; + u32 win1_reservd0; + u32 reserved2[48]; + u32 post_dsp_hact_info; + u32 post_dsp_vact_info; + u32 post_scl_factor_yrgb; + u32 post_reserved; + u32 post_scl_ctrl; + u32 post_dsp_vact_info_f1; + u32 dsp_htotal_hs_end; + u32 dsp_hact_st_end; + u32 dsp_vtotal_vs_end; + u32 dsp_vact_st_end; + u32 dsp_vs_st_end_f1; + u32 dsp_vact_st_end_f1; +}; +check_member(rk3288_vop, dsp_vact_st_end_f1, 0x19c); + +enum rockchip_fb_data_format_t { + ARGB8888 = 0, + RGB888 = 1, + RGB565 = 2, +}; + +enum { + LB_YUV_3840X5 = 0x0, + LB_YUV_2560X8 = 0x1, + LB_RGB_3840X2 = 0x2, + LB_RGB_2560X4 = 0x3, + LB_RGB_1920X5 = 0x4, + LB_RGB_1280X8 = 0x5 +}; + +enum vop_modes { + VOP_MODE_EDP = 0, + VOP_MODE_HDMI, + VOP_MODE_NONE, + VOP_MODE_AUTO_DETECT, + VOP_MODE_UNKNOWN, +}; + +/* VOP_VERSION_INFO */ +#define M_FPGA_VERSION (0xffff << 16) +#define M_RTL_VERSION (0xffff) + +/* VOP_SYS_CTRL */ +#define M_AUTO_GATING_EN (1 << 23) +#define M_STANDBY_EN (1 << 22) +#define M_DMA_STOP (1 << 21) +#define M_MMU_EN (1 << 20) +#define M_DAM_BURST_LENGTH (0x3 << 18) +#define M_MIPI_OUT_EN (1 << 15) +#define M_EDP_OUT_EN (1 << 14) +#define M_HDMI_OUT_EN (1 << 13) +#define M_RGB_OUT_EN (1 << 12) +#define M_ALL_OUT_EN \ + (M_MIPI_OUT_EN | M_EDP_OUT_EN | M_HDMI_OUT_EN | M_RGB_OUT_EN) +#define M_EDPI_WMS_FS (1 << 10) +#define M_EDPI_WMS_MODE (1 << 9) +#define M_EDPI_HALT_EN (1 << 8) +#define M_DOUB_CH_OVERLAP_NUM (0xf << 4) +#define M_DOUB_CHANNEL_EN (1 << 3) +#define M_DIRECT_PATH_LAYER_SEL (0x3 << 1) +#define M_DIRECT_PATH_EN (1) + +#define V_AUTO_GATING_EN(x) (((x) & 1) << 23) +#define V_STANDBY_EN(x) (((x) & 1) << 22) +#define V_DMA_STOP(x) (((x) & 1) << 21) +#define V_MMU_EN(x) (((x) & 1) << 20) +#define V_DMA_BURST_LENGTH(x) (((x) & 3) << 18) +#define V_MIPI_OUT_EN(x) (((x) & 1) << 15) +#define V_EDP_OUT_EN(x) (((x) & 1) << 14) +#define V_HDMI_OUT_EN(x) (((x) & 1) << 13) +#define V_RGB_OUT_EN(x) (((x) & 1) << 12) +#define V_EDPI_WMS_FS(x) (((x) & 1) << 10) +#define V_EDPI_WMS_MODE(x) (((x) & 1) << 9) +#define V_EDPI_HALT_EN(x) (((x)&1)<<8) +#define V_DOUB_CH_OVERLAP_NUM(x) (((x) & 0xf) << 4) +#define V_DOUB_CHANNEL_EN(x) (((x) & 1) << 3) +#define V_DIRECT_PATH_LAYER_SEL(x) (((x) & 3) << 1) +#define V_DIRECT_PATH_EN(x) ((x) & 1) + +/* VOP_SYS_CTRL1 */ +#define M_AXI_OUTSTANDING_MAX_NUM (0x1f << 13) +#define M_AXI_MAX_OUTSTANDING_EN (1 << 12) +#define M_NOC_WIN_QOS (3 << 10) +#define M_NOC_QOS_EN (1 << 9) +#define M_NOC_HURRY_THRESHOLD (0x3f << 3) +#define M_NOC_HURRY_VALUE (0x3 << 1) +#define M_NOC_HURRY_EN (1) + +#define V_AXI_OUTSTANDING_MAX_NUM(x) (((x) & 0x1f) << 13) +#define V_AXI_MAX_OUTSTANDING_EN(x) (((x) & 1) << 12) +#define V_NOC_WIN_QOS(x) (((x) & 3) << 10) +#define V_NOC_QOS_EN(x) (((x) & 1) << 9) +#define V_NOC_HURRY_THRESHOLD(x) (((x) & 0x3f) << 3) +#define V_NOC_HURRY_VALUE(x) (((x) & 3) << 1) +#define V_NOC_HURRY_EN(x) ((x) & 1) + +/* VOP_DSP_CTRL0 */ +#define M_DSP_Y_MIR_EN (1 << 23) +#define M_DSP_X_MIR_EN (1 << 22) +#define M_DSP_YUV_CLIP (1 << 21) +#define M_DSP_CCIR656_AVG (1 << 20) +#define M_DSP_BLACK_EN (1 << 19) +#define M_DSP_BLANK_EN (1 << 18) +#define M_DSP_OUT_ZERO (1 << 17) +#define M_DSP_DUMMY_SWAP (1 << 16) +#define M_DSP_DELTA_SWAP (1 << 15) +#define M_DSP_RG_SWAP (1 << 14) +#define M_DSP_RB_SWAP (1 << 13) +#define M_DSP_BG_SWAP (1 << 12) +#define M_DSP_FIELD_POL (1 << 11) +#define M_DSP_INTERLACE (1 << 10) +#define M_DSP_DDR_PHASE (1 << 9) +#define M_DSP_DCLK_DDR (1 << 8) +#define M_DSP_DCLK_POL (1 << 7) +#define M_DSP_DEN_POL (1 << 6) +#define M_DSP_VSYNC_POL (1 << 5) +#define M_DSP_HSYNC_POL (1 << 4) +#define M_DSP_OUT_MODE (0xf) + +#define V_DSP_Y_MIR_EN(x) (((x) & 1) << 23) +#define V_DSP_X_MIR_EN(x) (((x) & 1) << 22) +#define V_DSP_YUV_CLIP(x) (((x) & 1) << 21) +#define V_DSP_CCIR656_AVG(x) (((x) & 1) << 20) +#define V_DSP_BLACK_EN(x) (((x) & 1) << 19) +#define V_DSP_BLANK_EN(x) (((x) & 1) << 18) +#define V_DSP_OUT_ZERO(x) (((x) & 1) << 17) +#define V_DSP_DUMMY_SWAP(x) (((x) & 1) << 16) +#define V_DSP_DELTA_SWAP(x) (((x) & 1) << 15) +#define V_DSP_RG_SWAP(x) (((x) & 1) << 14) +#define V_DSP_RB_SWAP(x) (((x) & 1) << 13) +#define V_DSP_BG_SWAP(x) (((x) & 1) << 12) +#define V_DSP_FIELD_POL(x) (((x) & 1) << 11) +#define V_DSP_INTERLACE(x) (((x) & 1) << 10) +#define V_DSP_DDR_PHASE(x) (((x) & 1) << 9) +#define V_DSP_DCLK_DDR(x) (((x) & 1) << 8) +#define V_DSP_DCLK_POL(x) (((x) & 1) << 7) +#define V_DSP_DEN_POL(x) (((x) & 1) << 6) +#define V_DSP_VSYNC_POL(x) (((x) & 1) << 5) +#define V_DSP_HSYNC_POL(x) (((x) & 1) << 4) +#define V_DSP_OUT_MODE(x) ((x) & 0xf) + +/* VOP_DSP_CTRL1 */ +#define M_DSP_LAYER3_SEL (3 << 14) +#define M_DSP_LAYER2_SEL (3 << 12) +#define M_DSP_LAYER1_SEL (3 << 10) +#define M_DSP_LAYER0_SEL (3 << 8) +#define M_DITHER_UP_EN (1 << 6) +#define M_DITHER_DOWN_SEL (1 << 4) +#define M_DITHER_DOWN_MODE (1 << 3) +#define M_DITHER_DOWN_EN (1 << 2) +#define M_PRE_DITHER_DOWN_EN (1 << 1) +#define M_DSP_LUT_EN (1) + +#define V_DSP_LAYER3_SEL(x) (((x) & 3) << 14) +#define V_DSP_LAYER2_SEL(x) (((x) & 3) << 12) +#define V_DSP_LAYER1_SEL(x) (((x) & 3) << 10) +#define V_DSP_LAYER0_SEL(x) (((x) & 3) << 8) +#define V_DITHER_UP_EN(x) (((x) & 1) << 6) +#define V_DITHER_DOWN_SEL(x) (((x) & 1) << 4) +#define V_DITHER_DOWN_MODE(x) (((x) & 1) << 3) +#define V_DITHER_DOWN_EN(x) (((x) & 1) << 2) +#define V_PRE_DITHER_DOWN_EN(x) (((x) & 1) << 1) +#define V_DSP_LUT_EN(x) ((x)&1) + +/* VOP_DSP_BG */ +#define M_DSP_BG_RED (0x3f << 20) +#define M_DSP_BG_GREEN (0x3f << 10) +#define M_DSP_BG_BLUE (0x3f << 0) + +#define V_DSP_BG_RED(x) (((x) & 0x3f) << 20) +#define V_DSP_BG_GREEN(x) (((x) & 0x3f) << 10) +#define V_DSP_BG_BLUE(x) (((x) & 0x3f) << 0) + +/* VOP_WIN0_CTRL0 */ +#define M_WIN0_YUV_CLIP (1 << 20) +#define M_WIN0_CBR_DEFLICK (1 << 19) +#define M_WIN0_YRGB_DEFLICK (1 << 18) +#define M_WIN0_PPAS_ZERO_EN (1 << 16) +#define M_WIN0_UV_SWAP (1 << 15) +#define M_WIN0_MID_SWAP (1 << 14) +#define M_WIN0_ALPHA_SWAP (1 << 13) +#define M_WIN0_RB_SWAP (1 << 12) +#define M_WIN0_CSC_MODE (3 << 10) +#define M_WIN0_NO_OUTSTANDING (1 << 9) +#define M_WIN0_INTERLACE_READ (1 << 8) +#define M_WIN0_LB_MODE (7 << 5) +#define M_WIN0_FMT_10 (1 << 4) +#define M_WIN0_DATA_FMT (7 << 1) +#define M_WIN0_EN (1 << 0) + +#define V_WIN0_YUV_CLIP(x) (((x) & 1) << 20) +#define V_WIN0_CBR_DEFLICK(x) (((x) & 1) << 19) +#define V_WIN0_YRGB_DEFLICK(x) (((x) & 1) << 18) +#define V_WIN0_PPAS_ZERO_EN(x) (((x) & 1) << 16) +#define V_WIN0_UV_SWAP(x) (((x) & 1) << 15) +#define V_WIN0_MID_SWAP(x) (((x) & 1) << 14) +#define V_WIN0_ALPHA_SWAP(x) (((x) & 1) << 13) +#define V_WIN0_RB_SWAP(x) (((x) & 1) << 12) +#define V_WIN0_CSC_MODE(x) (((x) & 3) << 10) +#define V_WIN0_NO_OUTSTANDING(x) (((x) & 1) << 9) +#define V_WIN0_INTERLACE_READ(x) (((x) & 1) << 8) +#define V_WIN0_LB_MODE(x) (((x) & 7) << 5) +#define V_WIN0_FMT_10(x) (((x) & 1) << 4) +#define V_WIN0_DATA_FMT(x) (((x) & 7) << 1) +#define V_WIN0_EN(x) ((x) & 1) + +/* VOP_WIN0_CTRL1 */ +#define M_WIN0_CBR_VSD_MODE (1 << 31) +#define M_WIN0_CBR_VSU_MODE (1 << 30) +#define M_WIN0_CBR_HSD_MODE (3 << 28) +#define M_WIN0_CBR_VER_SCL_MODE (3 << 26) +#define M_WIN0_CBR_HOR_SCL_MODE (3 << 24) +#define M_WIN0_YRGB_VSD_MODE (1 << 23) +#define M_WIN0_YRGB_VSU_MODE (1 << 22) +#define M_WIN0_YRGB_HSD_MODE (3 << 20) +#define M_WIN0_YRGB_VER_SCL_MODE (3 << 18) +#define M_WIN0_YRGB_HOR_SCL_MODE (3 << 16) +#define M_WIN0_LINE_LOAD_MODE (1 << 15) +#define M_WIN0_CBR_AXI_GATHER_NUM (7 << 12) +#define M_WIN0_YRGB_AXI_GATHER_NUM (0xf << 8) +#define M_WIN0_VSD_CBR_GT2 (1 << 7) +#define M_WIN0_VSD_CBR_GT4 (1 << 6) +#define M_WIN0_VSD_YRGB_GT2 (1 << 5) +#define M_WIN0_VSD_YRGB_GT4 (1 << 4) +#define M_WIN0_BIC_COE_SEL (3 << 2) +#define M_WIN0_CBR_AXI_GATHER_EN (1 << 1) +#define M_WIN0_YRGB_AXI_GATHER_EN (1) + +#define V_WIN0_CBR_VSD_MODE(x) (((x) & 1) << 31) +#define V_WIN0_CBR_VSU_MODE(x) (((x) & 1) << 30) +#define V_WIN0_CBR_HSD_MODE(x) (((x) & 3) << 28) +#define V_WIN0_CBR_VER_SCL_MODE(x) (((x) & 3) << 26) +#define V_WIN0_CBR_HOR_SCL_MODE(x) (((x) & 3) << 24) +#define V_WIN0_YRGB_VSD_MODE(x) (((x) & 1) << 23) +#define V_WIN0_YRGB_VSU_MODE(x) (((x) & 1) << 22) +#define V_WIN0_YRGB_HSD_MODE(x) (((x) & 3) << 20) +#define V_WIN0_YRGB_VER_SCL_MODE(x) (((x) & 3) << 18) +#define V_WIN0_YRGB_HOR_SCL_MODE(x) (((x) & 3) << 16) +#define V_WIN0_LINE_LOAD_MODE(x) (((x) & 1) << 15) +#define V_WIN0_CBR_AXI_GATHER_NUM(x) (((x) & 7) << 12) +#define V_WIN0_YRGB_AXI_GATHER_NUM(x) (((x) & 0xf) << 8) +#define V_WIN0_VSD_CBR_GT2(x) (((x) & 1) << 7) +#define V_WIN0_VSD_CBR_GT4(x) (((x) & 1) << 6) +#define V_WIN0_VSD_YRGB_GT2(x) (((x) & 1) << 5) +#define V_WIN0_VSD_YRGB_GT4(x) (((x) & 1) << 4) +#define V_WIN0_BIC_COE_SEL(x) (((x) & 3) << 2) +#define V_WIN0_CBR_AXI_GATHER_EN(x) (((x) & 1) << 1) +#define V_WIN0_YRGB_AXI_GATHER_EN(x) ((x) & 1) + +/*VOP_WIN0_COLOR_KEY*/ +#define M_WIN0_KEY_EN (1 << 31) +#define M_WIN0_KEY_COLOR (0x3fffffff) + +#define V_WIN0_KEY_EN(x) (((x) & 1) << 31) +#define V_WIN0_KEY_COLOR(x) ((x) & 0x3fffffff) + +/* VOP_WIN0_VIR */ +#define V_ARGB888_VIRWIDTH(x) (((x) & 0x3fff) << 0) +#define V_RGB888_VIRWIDTH(x) (((((x * 3) >> 2)+((x) % 3)) & 0x3fff) << 0) +#define V_RGB565_VIRWIDTH(x) (((x / 2) & 0x3fff) << 0) +#define YUV_VIRWIDTH(x) (((x / 4) & 0x3fff) << 0) + +/* VOP_WIN0_ACT_INFO */ +#define V_ACT_HEIGHT(x) (((x) & 0x1fff) << 16) +#define V_ACT_WIDTH(x) ((x) & 0x1fff) + +/* VOP_WIN0_DSP_INFO */ +#define V_DSP_HEIGHT(x) (((x) & 0xfff) << 16) +#define V_DSP_WIDTH(x) ((x) & 0xfff) + +/* VOP_WIN0_DSP_ST */ +#define V_DSP_YST(x) (((x) & 0x1fff) << 16) +#define V_DSP_XST(x) ((x) & 0x1fff) + +/* VOP_WIN0_SCL_OFFSET */ +#define V_WIN0_VS_OFFSET_CBR(x) (((x) & 0xff) << 24) +#define V_WIN0_VS_OFFSET_YRGB(x) (((x) & 0xff) << 16) +#define V_WIN0_HS_OFFSET_CBR(x) (((x) & 0xff) << 8) +#define V_WIN0_HS_OFFSET_YRGB(x) ((x) & 0xff) + +#define V_HSYNC(x) (((x)&0x1fff)<<0) /* hsync pulse width */ +#define V_HORPRD(x) (((x)&0x1fff)<<16) /* horizontal period */ +#define V_VSYNC(x) (((x)&0x1fff)<<0) +#define V_VERPRD(x) (((x)&0x1fff)<<16) + +#define V_HEAP(x) (((x)&0x1fff)<<0)/* horizontal active end */ +#define V_HASP(x) (((x)&0x1fff)<<16)/* horizontal active start */ +#define V_VAEP(x) (((x)&0x1fff)<<0) +#define V_VASP(x) (((x)&0x1fff)<<16) + +#endif diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index 5566719..0e9a8ac 100644 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -5,4 +5,4 @@ # SPDX-License-Identifier: GPL-2.0+ #
-obj-y += rk_edp.o rk_hdmi.o +obj-y += rk_edp.o rk_hdmi.o rk_vop.o diff --git a/drivers/video/rockchip/rk_vop.c b/drivers/video/rockchip/rk_vop.c new file mode 100644 index 0000000..adbc68f --- /dev/null +++ b/drivers/video/rockchip/rk_vop.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2015 Google, Inc + * Copyright 2014 Rockchip Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <display.h> +#include <dm.h> +#include <edid.h> +#include <regmap.h> +#include <syscon.h> +#include <video.h> +#include <asm/gpio.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/cru_rk3288.h> +#include <asm/arch/grf_rk3288.h> +#include <asm/arch/edp_rk3288.h> +#include <asm/arch/hdmi_rk3288.h> +#include <asm/arch/vop_rk3288.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <dt-bindings/clock/rk3288-cru.h> +#include <power/regulator.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct rk_vop_priv { + struct rk3288_vop *regs; + struct rk3288_grf *grf; +}; + +void rkvop_enable(struct rk3288_vop *regs, ulong fbbase, + int fb_bits_per_pixel, const struct display_timing *edid) +{ + u32 lb_mode; + u32 rgb_mode; + u32 hactive = edid->hactive.typ; + u32 vactive = edid->vactive.typ; + + writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1), + ®s->win0_act_info); + + writel(V_DSP_XST(edid->hsync_len.typ + edid->hback_porch.typ) | + V_DSP_YST(edid->vsync_len.typ + edid->vback_porch.typ), + ®s->win0_dsp_st); + + writel(V_DSP_WIDTH(hactive - 1) | + V_DSP_HEIGHT(vactive - 1), + ®s->win0_dsp_info); + + clrsetbits_le32(®s->win0_color_key, M_WIN0_KEY_EN | M_WIN0_KEY_COLOR, + V_WIN0_KEY_EN(0) | V_WIN0_KEY_COLOR(0)); + + switch (fb_bits_per_pixel) { + case 16: + rgb_mode = RGB565; + writel(V_RGB565_VIRWIDTH(hactive), ®s->win0_vir); + break; + case 24: + rgb_mode = RGB888; + writel(V_RGB888_VIRWIDTH(hactive), ®s->win0_vir); + break; + case 32: + default: + rgb_mode = ARGB8888; + writel(V_ARGB888_VIRWIDTH(hactive), ®s->win0_vir); + break; + } + + if (hactive > 2560) + lb_mode = LB_RGB_3840X2; + else if (hactive > 1920) + lb_mode = LB_RGB_2560X4; + else if (hactive > 1280) + lb_mode = LB_RGB_1920X5; + else + lb_mode = LB_RGB_1280X8; + + clrsetbits_le32(®s->win0_ctrl0, + M_WIN0_LB_MODE | M_WIN0_DATA_FMT | M_WIN0_EN, + V_WIN0_LB_MODE(lb_mode) | V_WIN0_DATA_FMT(rgb_mode) | + V_WIN0_EN(1)); + + writel(fbbase, ®s->win0_yrgb_mst); + writel(0x01, ®s->reg_cfg_done); /* enable reg config */ +} + +void rkvop_mode_set(struct rk3288_vop *regs, + const struct display_timing *edid, enum vop_modes mode) +{ + u32 hactive = edid->hactive.typ; + u32 vactive = edid->vactive.typ; + u32 hsync_len = edid->hsync_len.typ; + u32 hback_porch = edid->hback_porch.typ; + u32 vsync_len = edid->vsync_len.typ; + u32 vback_porch = edid->vback_porch.typ; + u32 hfront_porch = edid->hfront_porch.typ; + u32 vfront_porch = edid->vfront_porch.typ; + uint flags; + + switch (mode) { + case VOP_MODE_HDMI: + clrsetbits_le32(®s->sys_ctrl, M_ALL_OUT_EN, + V_HDMI_OUT_EN(1)); + break; + case VOP_MODE_EDP: + default: + clrsetbits_le32(®s->sys_ctrl, M_ALL_OUT_EN, + V_EDP_OUT_EN(1)); + break; + } + + flags = V_DSP_OUT_MODE(15) | + V_DSP_HSYNC_POL(!!(edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)) | + V_DSP_VSYNC_POL(!!(edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)); + + clrsetbits_le32(®s->dsp_ctrl0, + M_DSP_OUT_MODE | M_DSP_VSYNC_POL | M_DSP_HSYNC_POL, + flags); + + writel(V_HSYNC(hsync_len) | + V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch), + ®s->dsp_htotal_hs_end); + + writel(V_HEAP(hsync_len + hback_porch + hactive) | + V_HASP(hsync_len + hback_porch), + ®s->dsp_hact_st_end); + + writel(V_VSYNC(vsync_len) | + V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch), + ®s->dsp_vtotal_vs_end); + + writel(V_VAEP(vsync_len + vback_porch + vactive)| + V_VASP(vsync_len + vback_porch), + ®s->dsp_vact_st_end); + + writel(V_HEAP(hsync_len + hback_porch + hactive) | + V_HASP(hsync_len + hback_porch), + ®s->post_dsp_hact_info); + + writel(V_VAEP(vsync_len + vback_porch + vactive)| + V_VASP(vsync_len + vback_porch), + ®s->post_dsp_vact_info); + + writel(0x01, ®s->reg_cfg_done); /* enable reg config */ +} + +/** + * rk_display_init() - Try to enable the given display device + * + * This function performs many steps: + * - Finds the display device being referenced by @ep_node + * - Puts the VOP's ID into its uclass platform data + * - Probes the device to set it up + * - Reads the EDID timing information + * - Sets up the VOP clocks, etc. for the selected pixel clock and display mode + * - Enables the display (the display device handles this and will do different + * things depending on the display type) + * - Tells the uclass about the display resolution so that the console will + * appear correctly + * + * @dev: VOP device that we want to connect to the display + * @fbbase: Frame buffer address + * @l2bpp Log2 of bits-per-pixels for the display + * @ep_node: Device tree node to process - this is the offset of an endpoint + * node within the VOP's 'port' list. + * @return 0 if OK, -ve if something went wrong + */ +int rk_display_init(struct udevice *dev, ulong fbbase, + enum video_log2_bpp l2bpp, int ep_node) +{ + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + const void *blob = gd->fdt_blob; + struct rk_vop_priv *priv = dev_get_priv(dev); + int vop_id, remote_vop_id; + struct rk3288_vop *regs = priv->regs; + struct display_timing timing; + struct udevice *disp; + int ret, remote, i, offset; + struct display_plat *disp_uc_plat; + struct udevice *clk; + + vop_id = fdtdec_get_int(blob, ep_node, "reg", -1); + debug("vop_id=%d\n", vop_id); + remote = fdtdec_lookup_phandle(blob, ep_node, "remote-endpoint"); + if (remote < 0) + return -EINVAL; + remote_vop_id = fdtdec_get_int(blob, remote, "reg", -1); + debug("remote vop_id=%d\n", remote_vop_id); + + for (i = 0, offset = remote; i < 3 && offset > 0; i++) + offset = fdt_parent_offset(blob, offset); + if (offset < 0) { + debug("%s: Invalid remote-endpoint position\n", dev->name); + return -EINVAL; + } + + ret = uclass_find_device_by_of_offset(UCLASS_DISPLAY, offset, &disp); + if (ret) { + debug("%s: device '%s' display not found (ret=%d)\n", __func__, + dev->name, ret); + return ret; + } + + disp_uc_plat = dev_get_uclass_platdata(disp); + debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat); + disp_uc_plat->source_id = remote_vop_id; + disp_uc_plat->src_dev = dev; + + ret = device_probe(disp); + if (ret) { + debug("%s: device '%s' display won't probe (ret=%d)\n", + __func__, dev->name, ret); + return ret; + } + + ret = display_read_timing(disp, &timing); + if (ret) { + debug("%s: Failed to read timings\n", __func__); + return ret; + } + + ret = rkclk_get_clk(CLK_NEW, &clk); + if (!ret) { + ret = clk_set_periph_rate(clk, DCLK_VOP0 + vop_id, + timing.pixelclock.typ); + } + if (ret) { + debug("%s: Failed to set pixel clock: ret=%d\n", __func__, ret); + return ret; + } + + rkvop_mode_set(regs, &timing, vop_id); + + rkvop_enable(regs, fbbase, 1 << l2bpp, &timing); + + ret = display_enable(disp, 1 << l2bpp, &timing); + if (ret) + return ret; + + uc_priv->xsize = timing.hactive.typ; + uc_priv->ysize = timing.vactive.typ; + uc_priv->bpix = l2bpp; + debug("fb=%lx, size=%d %d\n", fbbase, uc_priv->xsize, uc_priv->ysize); + + return 0; +} + +static int rk_vop_probe(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + const void *blob = gd->fdt_blob; + struct rk_vop_priv *priv = dev_get_priv(dev); + struct udevice *reg; + int ret, port, node; + + /* Before relocation we don't need to do anything */ + if (!(gd->flags & GD_FLG_RELOC)) + return 0; + + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + priv->regs = (struct rk3288_vop *)dev_get_addr(dev); + + /* lcdc(vop) iodomain select 1.8V */ + rk_setreg(&priv->grf->io_vsel, 1 << 0); + + /* + * Try some common regulators. We should really get these from the + * device tree somehow. + */ + ret = regulator_autoset_by_name("vcc18_lcd", ®); + if (ret) + debug("%s: Cannot autoset regulator vcc18_lcd\n", __func__); + ret = regulator_autoset_by_name("VCC18_LCD", ®); + if (ret) + debug("%s: Cannot autoset regulator VCC18_LCD\n", __func__); + ret = regulator_autoset_by_name("vdd10_lcd_pwren_h", ®); + if (ret) { + debug("%s: Cannot autoset regulator vdd10_lcd_pwren_h\n", + __func__); + } + ret = regulator_autoset_by_name("vdd10_lcd", ®); + if (ret) { + debug("%s: Cannot autoset regulator vdd10_lcd\n", + __func__); + } + ret = regulator_autoset_by_name("VDD10_LCD", ®); + if (ret) { + debug("%s: Cannot autoset regulator VDD10_LCD\n", + __func__); + } + ret = regulator_autoset_by_name("vcc33_lcd", ®); + if (ret) + debug("%s: Cannot autoset regulator vcc33_lcd\n", __func__); + + /* + * Try all the ports until we find one that works. In practice this + * tries EDP first if available, then HDMI. + */ + port = fdt_subnode_offset(blob, dev->of_offset, "port"); + if (port < 0) + return -EINVAL; + for (node = fdt_first_subnode(blob, port); + node > 0; + node = fdt_next_subnode(blob, node)) { + ret = rk_display_init(dev, plat->base, VIDEO_BPP16, node); + if (ret) + debug("Device failed: ret=%d\n", ret); + if (!ret) + break; + } + + return ret; +} + +static int rk_vop_bind(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + + plat->size = 1920 * 1080 * 2; + + return 0; +} + +static const struct video_ops rk_vop_ops = { +}; + +static const struct udevice_id rk_vop_ids[] = { + { .compatible = "rockchip,rk3288-vop" }, + { } +}; + +U_BOOT_DRIVER(rk_vop) = { + .name = "rk_vop", + .id = UCLASS_VIDEO, + .of_match = rk_vop_ids, + .ops = &rk_vop_ops, + .bind = rk_vop_bind, + .probe = rk_vop_probe, + .priv_auto_alloc_size = sizeof(struct rk_vop_priv), +};

At present the low-level init is skipped on rockchip. Among other things this means that the instruction cache is left disabled. Fix this.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/mach-rockchip/board.c | 4 ++++ arch/arm/mach-rockchip/rk3288-board-spl.c | 4 ++++ include/configs/rk3288_common.h | 1 - 3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-rockchip/board.c b/arch/arm/mach-rockchip/board.c index f026abf..380aa91 100644 --- a/arch/arm/mach-rockchip/board.c +++ b/arch/arm/mach-rockchip/board.c @@ -45,3 +45,7 @@ void enable_caches(void) dcache_enable(); } #endif + +void lowlevel_init(void) +{ +} diff --git a/arch/arm/mach-rockchip/rk3288-board-spl.c b/arch/arm/mach-rockchip/rk3288-board-spl.c index b2c5729..f61eeab 100644 --- a/arch/arm/mach-rockchip/rk3288-board-spl.c +++ b/arch/arm/mach-rockchip/rk3288-board-spl.c @@ -270,3 +270,7 @@ err: /* No way to report error here */ hang(); } + +void lowlevel_init(void) +{ +} diff --git a/include/configs/rk3288_common.h b/include/configs/rk3288_common.h index ebddfb0..b9ff5a5 100644 --- a/include/configs/rk3288_common.h +++ b/include/configs/rk3288_common.h @@ -16,7 +16,6 @@ #define CONFIG_BAUDRATE 115200 #define CONFIG_SYS_MALLOC_LEN (32 << 20) #define CONFIG_SYS_CBSIZE 1024 -#define CONFIG_SKIP_LOWLEVEL_INIT #define CONFIG_SYS_THUMB_BUILD #define CONFIG_OF_LIBFDT #define CONFIG_DISPLAY_BOARDINFO

Add a command that displays the PLLs and their current rate.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/mach-rockchip/board.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/arch/arm/mach-rockchip/board.c b/arch/arm/mach-rockchip/board.c index 380aa91..133d663 100644 --- a/arch/arm/mach-rockchip/board.c +++ b/arch/arm/mach-rockchip/board.c @@ -5,6 +5,7 @@ */
#include <common.h> +#include <clk.h> #include <dm.h> #include <ram.h> #include <asm/io.h> @@ -49,3 +50,26 @@ void enable_caches(void) void lowlevel_init(void) { } + +static int do_clock(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct udevice *dev; + + for (uclass_first_device(UCLASS_CLK, &dev); + dev; + uclass_next_device(&dev)) { + ulong rate; + + rate = clk_get_rate(dev); + printf("%s: %lu\n", dev->name, rate); + } + + return 0; +} + +U_BOOT_CMD( + clock, 2, 1, do_clock, + "display information about clocks", + "" +);

This script has proved useful for parsing datasheets and creating register shift/mask values for use in header files. Include it in case it is useful for others.
Signed-off-by: Simon Glass sjg@chromium.org ---
doc/README.rockchip | 6 ++ tools/rkmux.py | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100755 tools/rkmux.py
diff --git a/doc/README.rockchip b/doc/README.rockchip index 9a2ebca..7be5a13 100644 --- a/doc/README.rockchip +++ b/doc/README.rockchip @@ -243,6 +243,12 @@ SPI flash.
See above for instructions on how to write a SPI image.
+rkmux.py +-------- + +You can use this script to create #defines for SoC register access. See the +script for usage. +
Device tree and driver model ---------------------------- diff --git a/tools/rkmux.py b/tools/rkmux.py new file mode 100755 index 0000000..3917335 --- /dev/null +++ b/tools/rkmux.py @@ -0,0 +1,218 @@ +#!/usr/bin/python + +# Script to create enums from datasheet register tables +# +# Usage: +# +# First, create a text file from the datasheet: +# pdftotext -layout /path/to/rockchip-3288-trm.pdf /tmp/asc +# +# Then use this script to output the #defines for a particular register: +# ./tools/rkmux.py GRF_GPIO4C_IOMUX +# +# It will create output suitable for putting in a header file, with SHIFT and +# MASK values for each bitfield in the register. +# +# Note: this tool is not perfect and you may need to edit the resulting code. +# But it should speed up the process. + +import csv +import re +import sys + +tab_to_col = 3 + +class RegField: + def __init__(self, cols=None): + if cols: + self.bits, self.attr, self.reset_val, self.desc = ( + [x.strip() for x in cols]) + self.desc = [self.desc] + else: + self.bits = '' + self.attr = '' + self.reset_val = '' + self.desc = [] + + def Setup(self, cols): + self.bits, self.attr, self.reset_val = cols[0:3] + if len(cols) > 3: + self.desc.append(cols[3]) + + def AddDesc(self, desc): + self.desc.append(desc) + + def Show(self): + print self + print + self.__init__() + + def __str__(self): + return '%s,%s,%s,%s' % (self.bits, self.attr, self.reset_val, + '\n'.join(self.desc)) + +class Printer: + def __init__(self, name): + self.first = True + self.name = name + self.re_sel = re.compile("[1-9]'b([01]+): (.*)") + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + if not self.first: + self.output_footer() + + def output_header(self): + print '/* %s */' % self.name + print 'enum {' + + def output_footer(self): + print '};'; + + def output_regfield(self, regfield): + lines = regfield.desc + field = lines[0] + #print 'field:', field + if field in ['reserved', 'reserve', 'write_enable', 'write_mask']: + return + if field.endswith('_sel') or field.endswith('_con'): + field = field[:-4] + elif field.endswith(' iomux'): + field = field[:-6] + elif field.endswith('_mode') or field.endswith('_mask'): + field = field[:-5] + #else: + #print 'bad field %s' % field + #return + field = field.upper() + if ':' in regfield.bits: + bit_high, bit_low = [int(x) for x in regfield.bits.split(':')] + else: + bit_high = bit_low = int(regfield.bits) + bit_width = bit_high - bit_low + 1 + mask = (1 << bit_width) - 1 + if self.first: + self.first = False + self.output_header() + else: + print + out_enum(field, 'shift', bit_low) + out_enum(field, 'mask', mask) + next_val = -1 + #print 'lines: %s', lines + for line in lines: + m = self.re_sel.match(line) + if m: + val, enum = int(m.group(1), 2), m.group(2) + if enum not in ['reserved', 'reserve']: + out_enum(field, enum, val, val == next_val) + next_val = val + 1 + + +def process_file(name, fd): + field = RegField() + reg = '' + + fields = [] + + def add_it(field): + if field.bits: + if reg == name: + fields.append(field) + field = RegField() + return field + + def is_field_start(line): + if '=' in line or '+' in line: + return False + if (line.startswith('gpio') or line.startswith('peri_') or + line.endswith('_sel') or line.endswith('_con')): + return True + if not ' ' in line: # and '_' in line: + return True + return False + + for line in fd: + line = line.rstrip() + if line[:4] in ['GRF_', 'PMU_', 'CRU_']: + field = add_it(field) + reg = line + do_this = name == reg + elif not line or not line.startswith(' '): + continue + line = line.replace('\xe2\x80\x99', "'") + leading = len(line) - len(line.lstrip()) + line = line.lstrip() + cols = re.split(' *', line, 3) + if leading > 15 or (len(cols) > 3 and is_field_start(cols[3])): + if is_field_start(line): + field = add_it(field) + field.AddDesc(line) + else: + if cols[0] == 'Bit' or len(cols) < 3: + continue + #print + #print field + field = add_it(field) + field.Setup(cols) + field = add_it(field) + + with Printer(name) as printer: + for field in fields: + #print field + printer.output_regfield(field) + #print + +def out_enum(field, suffix, value, skip_val=False): + str = '%s_%s' % (field.upper(), suffix.upper()) + if not skip_val: + tabs = tab_to_col - len(str) / 8 + if value > 9: + val_str = '%#x' % value + else: + val_str = '%d' % value + + str += '%s= %s' % ('\t' * tabs, val_str) + print '\t%s,' % str + +# Process a CSV file, e.g. from tabula +def process_csv(name, fd): + reader = csv.reader(fd) + + rows = [] + + field = RegField() + for row in reader: + #print field.desc + if not row[0]: + field.desc.append(row[3]) + continue + if field.bits: + if field.bits != 'Bit': + rows.append(field) + #print row + field = RegField(row) + + with Printer(name) as printer: + for row in rows: + #print field + printer.output_regfield(row) + #print + +fname = sys.argv[1] +name = sys.argv[2] + +# Read output from pdftotext -layout +if 1: + with open(fname, 'r') as fd: + process_file(name, fd) + +# Use tabula +# It seems to be better at outputting text for an entire cell in one cell. +# But it does not always work. E.g. GRF_GPIO7CH_IOMUX. +# So there is no point in using it. +if 0: + with open(fname, 'r') as fd: + process_csv(name, fd)

Now that we have a pretty good GPIO driver, enable the 'gpio' command on all rockchip boards.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/configs/rk3288_common.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/configs/rk3288_common.h b/include/configs/rk3288_common.h index b9ff5a5..ebf1ab0 100644 --- a/include/configs/rk3288_common.h +++ b/include/configs/rk3288_common.h @@ -79,6 +79,7 @@
#define CONFIG_CMD_CACHE #define CONFIG_CMD_TIME +#define CONFIG_CMD_GPIO
#define CONFIG_SYS_SDRAM_BASE 0 #define CONFIG_NR_DRAM_BANKS 1

Fix spaces in two comments in this file.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/mach-rockchip/rk3288/sdram_rk3288.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c index 5da04b9..2a43574 100644 --- a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c +++ b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c @@ -405,7 +405,7 @@ static void set_bandwidth_ratio(const struct chan_info *chan, u32 channel, /* Data Byte disable*/ clrbits_le32(&publ->datx8[2].dxgcr, 1); clrbits_le32(&publ->datx8[3].dxgcr, 1); - /*disable DLL */ + /* disable DLL */ setbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLDIS); setbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLDIS); } else { @@ -416,7 +416,7 @@ static void set_bandwidth_ratio(const struct chan_info *chan, u32 channel, setbits_le32(&publ->datx8[2].dxgcr, 1); setbits_le32(&publ->datx8[3].dxgcr, 1);
- /*enable DLL */ + /* enable DLL */ clrbits_le32(&publ->datx8[2].dxdllcr, DXDLLCR_DLLDIS); clrbits_le32(&publ->datx8[3].dxdllcr, DXDLLCR_DLLDIS); /* reset DLL */

This is a shortcut to obtaining a register address. Use it where possible, to simplify the code.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/mach-rockchip/rk3288/sdram_rk3288.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c index 2a43574..074cf518 100644 --- a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c +++ b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c @@ -808,20 +808,9 @@ static int rk3288_dmc_probe(struct udevice *dev) priv->chan[1].msch = (struct rk3288_msch *) (regmap_get_range(map, 0) + 0x80);
- map = syscon_get_regmap_by_driver_data(ROCKCHIP_SYSCON_GRF); - if (IS_ERR(map)) - return PTR_ERR(map); - priv->grf = regmap_get_range(map, 0); - - map = syscon_get_regmap_by_driver_data(ROCKCHIP_SYSCON_SGRF); - if (IS_ERR(map)) - return PTR_ERR(map); - priv->sgrf = regmap_get_range(map, 0); - - map = syscon_get_regmap_by_driver_data(ROCKCHIP_SYSCON_PMU); - if (IS_ERR(map)) - return PTR_ERR(map); - priv->pmu = regmap_get_range(map, 0); + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + priv->sgrf = syscon_get_first_range(ROCKCHIP_SYSCON_SGRF); + priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU);
ret = regmap_init_mem(dev, &map); if (ret)

These work reasonable well, but there are a few errors:
- Brackets should be used to avoid unexpected side-effects - When setting bits, the corresponding upper 16 bits should be set also
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/include/asm/arch-rockchip/hardware.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/arch/arm/include/asm/arch-rockchip/hardware.h b/arch/arm/include/asm/arch-rockchip/hardware.h index d5af5b8..08a66ef 100644 --- a/arch/arm/include/asm/arch-rockchip/hardware.h +++ b/arch/arm/include/asm/arch-rockchip/hardware.h @@ -7,14 +7,15 @@ #ifndef _ASM_ARCH_HARDWARE_H #define _ASM_ARCH_HARDWARE_H
-#define RK_CLRSETBITS(clr, set) ((((clr) | (set)) << 16) | set) +#define RK_CLRSETBITS(clr, set) ((((clr) | (set)) << 16) | (set)) #define RK_SETBITS(set) RK_CLRSETBITS(0, set) #define RK_CLRBITS(clr) RK_CLRSETBITS(clr, 0)
#define TIMER7_BASE 0xff810020
-#define rk_clrsetreg(addr, clr, set) writel((clr) << 16 | (set), addr) +#define rk_clrsetreg(addr, clr, set) \ + writel(((clr) | (set)) << 16 | (set), addr) #define rk_clrreg(addr, clr) writel((clr) << 16, addr) -#define rk_setreg(addr, set) writel(set, addr) +#define rk_setreg(addr, set) writel((set) << 16 | (set), addr)
#endif

When the board does not use MMC SPL this code is a waste of space. Drop it.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/mach-rockchip/rk3288-board-spl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-rockchip/rk3288-board-spl.c b/arch/arm/mach-rockchip/rk3288-board-spl.c index f61eeab..9fdd37d 100644 --- a/arch/arm/mach-rockchip/rk3288-board-spl.c +++ b/arch/arm/mach-rockchip/rk3288-board-spl.c @@ -111,9 +111,9 @@ static void configure_l2ctlr(void) write_l2ctlr(l2ctlr); }
+#ifdef CONFIG_SPL_MMC_SUPPORT static int configure_emmc(struct udevice *pinctrl) { -#ifdef CONFIG_SPL_MMC_SUPPORT struct gpio_desc desc; int ret;
@@ -143,10 +143,10 @@ static int configure_emmc(struct udevice *pinctrl) debug("gpio value ret=%d\n", ret); return ret; } -#endif
return 0; } +#endif
void board_init_f(ulong dummy) { @@ -244,6 +244,7 @@ void spl_board_init(void) debug("%s: Cannot find pinctrl device\n", __func__); goto err; } +#ifdef CONFIG_SPL_MMC_SUPPORT ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_SDCARD); if (ret) { debug("%s: Failed to set up SD card\n", __func__); @@ -254,6 +255,7 @@ void spl_board_init(void) debug("%s: Failed to set up eMMC\n", __func__); goto err; } +#endif
/* Enable debug UART */ ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_UART_DBG);

There is a minor error in the SDRAM timing. It does not seem to affect anything so far. Fix it just in case.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/dts/rk3288-veyron.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/dts/rk3288-veyron.dtsi b/arch/arm/dts/rk3288-veyron.dtsi index a31e00e..c201e85 100644 --- a/arch/arm/dts/rk3288-veyron.dtsi +++ b/arch/arm/dts/rk3288-veyron.dtsi @@ -246,7 +246,7 @@ 666000 1200000 >; rockchip,num-channels = <2>; - rockchip,pctl-timing = <0x29a 0xc8 0x1f8 0x42 0x4e 0x4 0xea 0xa + rockchip,pctl-timing = <0x29a 0xc8 0x1f4 0x42 0x4e 0x4 0xea 0xa 0x5 0x0 0xa 0x7 0x19 0x24 0xa 0x7 0x5 0xa 0x5 0x200 0x5 0x10 0x40 0x0 0x1 0x7 0x7 0x4 0xc 0x43 0x100 0x0

Fix a number of small errors which were found in reviewing the clock code.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/clk/clk_rk3288.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c index 42d2f85..79951b6 100644 --- a/drivers/clk/clk_rk3288.c +++ b/drivers/clk/clk_rk3288.c @@ -83,8 +83,13 @@ enum { * peripheral bus pclk div: * aclk_bus: pclk_bus = 1:1 or 2:1 or 4:1 or 8:1 */ + PERI_SEL_PLL_MASK = 1, + PERI_SEL_PLL_SHIFT = 15, + PERI_SEL_CPLL = 0, + PERI_SEL_GPLL, + PERI_PCLK_DIV_SHIFT = 12, - PERI_PCLK_DIV_MASK = 7, + PERI_PCLK_DIV_MASK = 3,
/* peripheral bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */ PERI_HCLK_DIV_SHIFT = 8, @@ -160,13 +165,13 @@ static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id, uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000; uint output_hz = vco_hz / div->no;
- debug("PLL at %p: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n", - pll, div->nf, div->nr, div->no, vco_hz, output_hz); + debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n", + (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz); assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ && (div->no == 1 || !(div->no % 2)));
- /* enter rest */ + /* enter reset */ rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT);
rk_clrsetreg(&pll->con0, @@ -177,7 +182,7 @@ static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id,
udelay(10);
- /* return form rest */ + /* return from reset */ rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT);
return 0; @@ -199,7 +204,6 @@ static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, }; int cfg;
- debug("%s: cru=%p, grf=%p, hz=%u\n", __func__, cru, grf, hz); switch (hz) { case 300000000: cfg = 0; @@ -214,7 +218,7 @@ static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, cfg = 3; break; default: - debug("Unsupported SDRAM frequency, add to clock.c!"); + debug("Unsupported SDRAM frequency"); return -EINVAL; }
@@ -420,6 +424,7 @@ static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT | PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT | PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT, + PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT | pclk_div << PERI_PCLK_DIV_SHIFT | hclk_div << PERI_HCLK_DIV_SHIFT | aclk_div << PERI_ACLK_DIV_SHIFT); @@ -787,7 +792,7 @@ static const char *const clk_name[CLK_COUNT] = { "dpll", "cpll", "gpll", - "mpll", + "npll", };
static int rk3288_clk_bind(struct udevice *dev)

Since the device tree does not specify the EDID pinctrl option for HDMI we must set it manually. Fix the driver to handle this.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/pinctrl/rockchip/pinctrl_rk3288.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3288.c b/drivers/pinctrl/rockchip/pinctrl_rk3288.c index 8356786..7c769bd 100644 --- a/drivers/pinctrl/rockchip/pinctrl_rk3288.c +++ b/drivers/pinctrl/rockchip/pinctrl_rk3288.c @@ -503,6 +503,8 @@ static int rk3288_pinctrl_get_periph_id(struct udevice *dev, return PERIPH_ID_I2C4; case 65: return PERIPH_ID_I2C5; + case 103: + return PERIPH_ID_HDMI; }
return -ENOENT;

Add a feature which speeds up the CPU to full speed in SPL to minimise boot time. This is only supported for certain boards (at present only jerry).
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/dts/rk3288-veyron.dtsi | 2 + arch/arm/include/asm/arch-rockchip/clock.h | 5 +++ arch/arm/include/asm/arch-rockchip/cru_rk3288.h | 12 ++++++ arch/arm/mach-rockchip/rk3288/Kconfig | 9 ++++ arch/arm/mach-rockchip/rk3288/sdram_rk3288.c | 36 ++++++++++++++++ configs/chromebook_jerry_defconfig | 1 + drivers/clk/clk_rk3288.c | 56 +++++++++++++++++++++++++ include/configs/chromebook_jerry.h | 3 ++ 8 files changed, 124 insertions(+)
diff --git a/arch/arm/dts/rk3288-veyron.dtsi b/arch/arm/dts/rk3288-veyron.dtsi index c201e85..421d212 100644 --- a/arch/arm/dts/rk3288-veyron.dtsi +++ b/arch/arm/dts/rk3288-veyron.dtsi @@ -332,6 +332,7 @@ clock-frequency = <400000>; i2c-scl-falling-time-ns = <50>; /* 2.5ns measured */ i2c-scl-rising-time-ns = <100>; /* 45ns measured */ + u-boot,dm-pre-reloc;
rk808: pmic@1b { compatible = "rockchip,rk808"; @@ -344,6 +345,7 @@ rockchip,system-power-controller; wakeup-source; #clock-cells = <1>; + u-boot,dm-pre-reloc;
vcc1-supply = <&vcc33_sys>; vcc2-supply = <&vcc33_sys>; diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h index a9ea268..d66b26f 100644 --- a/arch/arm/include/asm/arch-rockchip/clock.h +++ b/arch/arm/include/asm/arch-rockchip/clock.h @@ -74,4 +74,9 @@ void *rockchip_get_cru(void); */ int rkclk_get_clk(enum rk_clk_id clk_id, struct udevice **devp);
+struct rk3288_cru; +struct rk3288_grf; + +void rkclk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf); + #endif diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3288.h b/arch/arm/include/asm/arch-rockchip/cru_rk3288.h index b0dea70..d2690c7 100644 --- a/arch/arm/include/asm/arch-rockchip/cru_rk3288.h +++ b/arch/arm/include/asm/arch-rockchip/cru_rk3288.h @@ -109,6 +109,18 @@ enum { SPI0_DIV_MASK = 0x7f, };
+/* CRU_CLKSEL37_CON */ +enum { + PCLK_CORE_DBG_DIV_SHIFT = 9, + PCLK_CORE_DBG_DIV_MASK = 0x1f, + + ATCLK_CORE_DIV_CON_SHIFT = 4, + ATCLK_CORE_DIV_CON_MASK = 0x1f, + + CLK_L2RAM_DIV_SHIFT = 0, + CLK_L2RAM_DIV_MASK = 7, +}; + /* CRU_CLKSEL39_CON */ enum { ACLK_HEVC_PLL_SHIFT = 0xe, diff --git a/arch/arm/mach-rockchip/rk3288/Kconfig b/arch/arm/mach-rockchip/rk3288/Kconfig index d0a7276..ed89c3e 100644 --- a/arch/arm/mach-rockchip/rk3288/Kconfig +++ b/arch/arm/mach-rockchip/rk3288/Kconfig @@ -16,6 +16,15 @@ config TARGET_CHROMEBOOK_JERRY WiFi. It includes a Chrome OS EC (Cortex-M3) to provide access to the keyboard and battery functions.
+config ROCKCHIP_FAST_SPL + bool "Change the CPU to full speed in SPL" + depends on TARGET_CHROMEBOOK_JERRY + help + Some boards want to boot as fast as possible. We can increase the + CPU frequency in SPL if the power supply is configured to the correct + voltage. This option is only available on boards which support it + and have the required PMIC code. + config SYS_SOC default "rockchip"
diff --git a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c index 074cf518..e9e2211 100644 --- a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c +++ b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c @@ -22,6 +22,8 @@ #include <asm/arch/pmu_rk3288.h> #include <asm/arch/sdram.h> #include <linux/err.h> +#include <power/regulator.h> +#include <power/rk808_pmic.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -748,6 +750,32 @@ size_t sdram_size_mb(struct rk3288_pmu *pmu) }
#ifdef CONFIG_SPL_BUILD +# ifdef CONFIG_ROCKCHIP_FAST_SPL +static int veyron_init(struct dram_info *priv) +{ + struct udevice *pmic; + int ret; + + ret = uclass_first_device(UCLASS_PMIC, &pmic); + if (ret) + return ret; + + /* Slowly raise to max CPU voltage to prevent overshoot */ + ret = rk808_spl_configure_buck(pmic, 1, 1200000); + if (ret) + return ret; + udelay(175);/* Must wait for voltage to stabilize, 2mV/us */ + ret = rk808_spl_configure_buck(pmic, 1, 1400000); + if (ret) + return ret; + udelay(100);/* Must wait for voltage to stabilize, 2mV/us */ + + rkclk_configure_cpu(priv->cru, priv->grf); + + return 0; +} +# endif + static int setup_sdram(struct udevice *dev) { struct dram_info *priv = dev_get_priv(dev); @@ -791,6 +819,14 @@ static int setup_sdram(struct udevice *dev) return -EINVAL; }
+# ifdef CONFIG_ROCKCHIP_FAST_SPL + if (!fdt_node_check_compatible(blob, 0, "google,veyron")) { + ret = veyron_init(priv); + if (ret) + return ret; + } +# endif + return sdram_init(priv, ¶ms); } #endif diff --git a/configs/chromebook_jerry_defconfig b/configs/chromebook_jerry_defconfig index b2672b8..526306f 100644 --- a/configs/chromebook_jerry_defconfig +++ b/configs/chromebook_jerry_defconfig @@ -3,6 +3,7 @@ CONFIG_ARCH_ROCKCHIP=y CONFIG_SYS_MALLOC_F_LEN=0x2000 CONFIG_ROCKCHIP_RK3288=y CONFIG_TARGET_CHROMEBOOK_JERRY=y +CONFIG_ROCKCHIP_FAST_SPL=y CONFIG_SPL_STACK_R_ADDR=0x80000 CONFIG_DM_KEYBOARD=y CONFIG_DEFAULT_DEVICE_TREE="rk3288-jerry" diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c index 79951b6..3e376b4 100644 --- a/drivers/clk/clk_rk3288.c +++ b/drivers/clk/clk_rk3288.c @@ -59,6 +59,16 @@ enum { /* PLL CON3 */ PLL_RESET_SHIFT = 5,
+ /* CLKSEL0 */ + CORE_SEL_PLL_MASK = 1, + CORE_SEL_PLL_SHIFT = 15, + A17_DIV_MASK = 0x1f, + A17_DIV_SHIFT = 8, + MP_DIV_MASK = 0xf, + MP_DIV_SHIFT = 4, + M0_DIV_MASK = 0xf, + M0_DIV_SHIFT = 0, + /* CLKSEL1: pd bus clk pll sel: codec or general */ PD_BUS_SEL_PLL_MASK = 15, PD_BUS_SEL_CPLL = 0, @@ -438,6 +448,52 @@ static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) } #endif
+void rkclk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf) +{ + /* pll enter slow-mode */ + rk_clrsetreg(&cru->cru_mode_con, + APLL_MODE_MASK << APLL_MODE_SHIFT, + APLL_MODE_SLOW << APLL_MODE_SHIFT); + + rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg); + + /* waiting for pll lock */ + while (!(readl(&grf->soc_status[1]) & SOCSTS_APLL_LOCK)) + udelay(1); + + /* + * core clock pll source selection and + * set up dependent divisors for MPAXI/M0AXI and ARM clocks. + * core clock select apll, apll clk = 1800MHz + * arm clk = 1800MHz, mpclk = 450MHz, m0clk = 900MHz + */ + rk_clrsetreg(&cru->cru_clksel_con[0], + CORE_SEL_PLL_MASK << CORE_SEL_PLL_SHIFT | + A17_DIV_MASK << A17_DIV_SHIFT | + MP_DIV_MASK << MP_DIV_SHIFT | + M0_DIV_MASK << M0_DIV_SHIFT, + 0 << A17_DIV_SHIFT | + 3 << MP_DIV_SHIFT | + 1 << M0_DIV_SHIFT); + + /* + * set up dependent divisors for L2RAM/ATCLK and PCLK clocks. + * l2ramclk = 900MHz, atclk = 450MHz, pclk_dbg = 450MHz + */ + rk_clrsetreg(&cru->cru_clksel_con[37], + CLK_L2RAM_DIV_MASK << CLK_L2RAM_DIV_SHIFT | + ATCLK_CORE_DIV_CON_MASK << ATCLK_CORE_DIV_CON_SHIFT | + PCLK_CORE_DBG_DIV_MASK >> PCLK_CORE_DBG_DIV_SHIFT, + 1 << CLK_L2RAM_DIV_SHIFT | + 3 << ATCLK_CORE_DIV_CON_SHIFT | + 3 << PCLK_CORE_DBG_DIV_SHIFT); + + /* PLL enter normal-mode */ + rk_clrsetreg(&cru->cru_mode_con, + APLL_MODE_MASK << APLL_MODE_SHIFT, + APLL_MODE_NORMAL << APLL_MODE_SHIFT); +} + /* Get pll rate by id */ static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru, enum rk_clk_id clk_id) diff --git a/include/configs/chromebook_jerry.h b/include/configs/chromebook_jerry.h index 6e32f2c..e07d057 100644 --- a/include/configs/chromebook_jerry.h +++ b/include/configs/chromebook_jerry.h @@ -24,4 +24,7 @@
#define CONFIG_KEYBOARD
+#define CONFIG_SPL_POWER_SUPPORT +#define CONFIG_SPL_I2C_SUPPORT + #endif

Add the 'time' and 'sf test' commands so that we can test SPI flash performance.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/configs/chromebook_jerry.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/include/configs/chromebook_jerry.h b/include/configs/chromebook_jerry.h index e07d057..2a0dad4 100644 --- a/include/configs/chromebook_jerry.h +++ b/include/configs/chromebook_jerry.h @@ -20,6 +20,9 @@ #define CONFIG_SPL_SPI_LOAD #define CONFIG_SPI_FLASH_GIGADEVICE
+#define CONFIG_CMD_SF_TEST +#define CONFIG_CMD_TIME + #undef CONFIG_SPL_GPIO_SUPPORT
#define CONFIG_KEYBOARD

Enable these devices using the VOPL video output device. We explicitly disable VOPB in the device tree to avoid it taking over. Since this device has an LCD display this comes up by default. If the display fails for some reason then it will attempt to use HDMI. It is possible to force it to fail (and thus fall back to HDMI) by puting 'return -EPERM' at the top of rk_edp_probe(). For now there is no easy way to select between the two.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/dts/rk3288-jerry.dts | 5 +++++ arch/arm/dts/rk3288.dtsi | 1 + configs/chromebook_jerry_defconfig | 6 ++++++ include/configs/chromebook_jerry.h | 8 ++++++-- 4 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/arch/arm/dts/rk3288-jerry.dts b/arch/arm/dts/rk3288-jerry.dts index da37ea8..2aa3b9f 100644 --- a/arch/arm/dts/rk3288-jerry.dts +++ b/arch/arm/dts/rk3288-jerry.dts @@ -108,6 +108,11 @@ pinctrl-0 = <&vcc50_hdmi_en>; };
+&vopb { + /* Disable this so that we use vopl */ + status = "disabled"; +}; + &edp { pinctrl-names = "default"; pinctrl-0 = <&edp_hpd>; diff --git a/arch/arm/dts/rk3288.dtsi b/arch/arm/dts/rk3288.dtsi index ac367f8..fb1d1f7 100644 --- a/arch/arm/dts/rk3288.dtsi +++ b/arch/arm/dts/rk3288.dtsi @@ -692,6 +692,7 @@ iommus = <&vopl_mmu>; power-domains = <&power RK3288_PD_VIO>; status = "disabled"; + u-boot,dm-pre-reloc; vopl_out: port { #address-cells = <1>; #size-cells = <0>; diff --git a/configs/chromebook_jerry_defconfig b/configs/chromebook_jerry_defconfig index 526306f..27e53c1 100644 --- a/configs/chromebook_jerry_defconfig +++ b/configs/chromebook_jerry_defconfig @@ -42,7 +42,10 @@ CONFIG_DM_PMIC=y # CONFIG_SPL_PMIC_CHILDREN is not set CONFIG_PMIC_RK808=y CONFIG_DM_REGULATOR=y +CONFIG_DM_REGULATOR_FIXED=y CONFIG_REGULATOR_RK808=y +CONFIG_DM_PWM=y +CONFIG_PWM_ROCKCHIP=y CONFIG_RAM=y CONFIG_SPL_RAM=y CONFIG_DEBUG_UART=y @@ -51,6 +54,9 @@ CONFIG_DEBUG_UART_CLOCK=24000000 CONFIG_DEBUG_UART_SHIFT=2 CONFIG_SYS_NS16550=y CONFIG_ROCKCHIP_SPI=y +CONFIG_DM_VIDEO=y +CONFIG_VIDEO_ROTATION=y +CONFIG_VIDEO_ROCKCHIP=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_USE_TINY_PRINTF=y CONFIG_CMD_DHRYSTONE=y diff --git a/include/configs/chromebook_jerry.h b/include/configs/chromebook_jerry.h index 2a0dad4..150e876 100644 --- a/include/configs/chromebook_jerry.h +++ b/include/configs/chromebook_jerry.h @@ -9,8 +9,8 @@
#define ROCKCHIP_DEVICE_SETTINGS \ "stdin=serial,cros-ec-keyb\0" \ - "stdout=serial\0" \ - "stderr=serial\0" + "stdout=serial,vidconsole\0" \ + "stderr=serial,vidconsole\0"
#include <configs/rk3288_common.h>
@@ -30,4 +30,8 @@ #define CONFIG_SPL_POWER_SUPPORT #define CONFIG_SPL_I2C_SUPPORT
+#define CONFIG_I2C_EDID +#define CONFIG_SYS_WHITE_ON_BLACK +#define CONFIG_CONSOLE_SCROLL_LINES 10 + #endif

Enable HDMI output and a console on firefly.
Signed-off-by: Simon Glass sjg@chromium.org ---
configs/firefly-rk3288_defconfig | 7 +++++++ include/configs/firefly-rk3288.h | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/configs/firefly-rk3288_defconfig b/configs/firefly-rk3288_defconfig index d8db532..15eb4d8 100644 --- a/configs/firefly-rk3288_defconfig +++ b/configs/firefly-rk3288_defconfig @@ -32,9 +32,13 @@ CONFIG_SPL_PINCTRL=y # CONFIG_SPL_PINCTRL_FULL is not set CONFIG_ROCKCHIP_PINCTRL=y CONFIG_DM_PMIC=y +# CONFIG_SPL_PMIC_CHILDREN is not set CONFIG_PMIC_ACT8846=y CONFIG_DM_REGULATOR=y +CONFIG_DM_REGULATOR_FIXED=y CONFIG_REGULATOR_ACT8846=y +CONFIG_DM_PWM=y +CONFIG_PWM_ROCKCHIP=y CONFIG_RAM=y CONFIG_SPL_RAM=y CONFIG_DEBUG_UART=y @@ -42,6 +46,9 @@ CONFIG_DEBUG_UART_BASE=0xff690000 CONFIG_DEBUG_UART_CLOCK=24000000 CONFIG_DEBUG_UART_SHIFT=2 CONFIG_SYS_NS16550=y +CONFIG_DM_VIDEO=y +CONFIG_VIDEO_ROTATION=y +CONFIG_VIDEO_ROCKCHIP=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_USE_TINY_PRINTF=y CONFIG_CMD_DHRYSTONE=y diff --git a/include/configs/firefly-rk3288.h b/include/configs/firefly-rk3288.h index 8ac6521..d6423e7 100644 --- a/include/configs/firefly-rk3288.h +++ b/include/configs/firefly-rk3288.h @@ -7,7 +7,10 @@ #ifndef __CONFIG_H #define __CONFIG_H
-#define ROCKCHIP_DEVICE_SETTINGS +#define ROCKCHIP_DEVICE_SETTINGS \ + "stdin=serial,cros-ec-keyb\0" \ + "stdout=serial,vidconsole\0" \ + "stderr=serial,vidconsole\0"
#include <configs/rk3288_common.h>
@@ -21,4 +24,8 @@ */ #define CONFIG_ENV_OFFSET (96 * 1024)
+#define CONFIG_I2C_EDID +#define CONFIG_SYS_WHITE_ON_BLACK +#define CONFIG_CONSOLE_SCROLL_LINES 10 + #endif

This has been added and we have references to it in the rock2 board. Add this node.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/dts/rk3288.dtsi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/arch/arm/dts/rk3288.dtsi b/arch/arm/dts/rk3288.dtsi index fb1d1f7..e51c75c 100644 --- a/arch/arm/dts/rk3288.dtsi +++ b/arch/arm/dts/rk3288.dtsi @@ -631,6 +631,21 @@ status = "disabled"; };
+ spdif: sound@ff88b0000 { + compatible = "rockchip,rk3288-spdif", "rockchip,rk3066-spdif"; + reg = <0xff8b0000 0x10000>; + #sound-dai-cells = <0>; + clock-names = "hclk", "mclk"; + clocks = <&cru HCLK_SPDIF8CH>, <&cru SCLK_SPDIF8CH>; + dmas = <&dmac_bus_s 3>; + dma-names = "tx"; + interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&spdif_tx>; + rockchip,grf = <&grf>; + status = "disabled"; + }; + i2s: i2s@ff890000 { compatible = "rockchip,rk3288-i2s", "rockchip,rk3066-i2s"; reg = <0xff890000 0x10000>; @@ -1417,6 +1432,12 @@ <4 3 3 &pcfg_pull_none>; }; }; + + spdif { + spdif_tx: spdif-tx { + rockchip,pins = <RK_GPIO6 11 RK_FUNC_1 &pcfg_pull_none>; + }; + }; };
power: power-controller {

Bring in the current device tree files for rock2 from linux/next commit 719d6c1. Hopefully this is the latest one.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/dts/rk3288-rock2-som.dtsi | 278 +++++++++++++++++++++++++++++++++++ arch/arm/dts/rk3288-rock2-square.dts | 180 +++++++++++++++++++++++ 2 files changed, 458 insertions(+) create mode 100644 arch/arm/dts/rk3288-rock2-som.dtsi create mode 100644 arch/arm/dts/rk3288-rock2-square.dts
diff --git a/arch/arm/dts/rk3288-rock2-som.dtsi b/arch/arm/dts/rk3288-rock2-som.dtsi new file mode 100644 index 0000000..1ece66f --- /dev/null +++ b/arch/arm/dts/rk3288-rock2-som.dtsi @@ -0,0 +1,278 @@ +/* + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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 file 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. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <dt-bindings/pwm/pwm.h> +#include "rk3288.dtsi" + +/ { + memory { + reg = <0x0 0x80000000>; + device_type = "memory"; + }; + + emmc_pwrseq: emmc-pwrseq { + compatible = "mmc-pwrseq-emmc"; + pinctrl-0 = <&emmc_reset>; + pinctrl-names = "default"; + reset-gpios = <&gpio3 9 GPIO_ACTIVE_LOW>; + }; + + ext_gmac: external-gmac-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; + clock-output-names = "ext_gmac"; + }; + + vcc_sys: vsys-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + }; +}; + +&cpu0 { + cpu0-supply = <&vdd_cpu>; +}; + +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + disable-wp; + non-removable; + num-slots = <1>; + mmc-pwrseq = <&emmc_pwrseq>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>; + vmmc-supply = <&vcc_io>; + status = "okay"; +}; + +&gmac { + assigned-clocks = <&cru SCLK_MAC>; + assigned-clock-parents = <&ext_gmac>; + clock_in_out = "input"; + phy-mode = "rgmii"; + phy-supply = <&vccio_pmu>; + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_pins &phy_rst>; + snps,reset-gpio = <&gpio4 8 GPIO_ACTIVE_LOW>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 30000>; + rx_delay = <0x10>; + tx_delay = <0x30>; +}; + +&i2c0 { + status = "okay"; + + act8846: act8846@5a { + compatible = "active-semi,act8846"; + reg = <0x5a>; + system-power-controller; + inl1-supply = <&vcc_io>; + inl2-supply = <&vcc_sys>; + inl3-supply = <&vcc_20>; + vp1-supply = <&vcc_sys>; + vp2-supply = <&vcc_sys>; + vp3-supply = <&vcc_sys>; + vp4-supply = <&vcc_sys>; + + regulators { + vcc_ddr: REG1 { + regulator-name = "VCC_DDR"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + vcc_io: REG2 { + regulator-name = "VCC_IO"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vdd_log: REG3 { + regulator-name = "VDD_LOG"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + vcc_20: REG4 { + regulator-name = "VCC_20"; + regulator-min-microvolt = <2000000>; + regulator-max-microvolt = <2000000>; + regulator-always-on; + }; + + vccio_sd: REG5 { + regulator-name = "VCCIO_SD"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vdd10_lcd: REG6 { + regulator-name = "VDD10_LCD"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + vcca_codec: REG7 { + regulator-name = "VCCA_CODEC"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vcca_tp: REG8 { + regulator-name = "VCCA_TP"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vccio_pmu: REG9 { + regulator-name = "VCCIO_PMU"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vdd_10: REG10 { + regulator-name = "VDD_10"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + vcc_18: REG11 { + regulator-name = "VCC_18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + vcc18_lcd: REG12 { + regulator-name = "VCC18_LCD"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + }; + }; + + vdd_cpu: syr827@40 { + compatible = "silergy,syr827"; + reg = <0x40>; + fcs,suspend-voltage-selector = <1>; + regulator-always-on; + regulator-boot-on; + regulator-enable-ramp-delay = <300>; + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <8000>; + vin-supply = <&vcc_sys>; + }; + + vdd_gpu: syr828@41 { + compatible = "silergy,syr828"; + reg = <0x41>; + fcs,suspend-voltage-selector = <1>; + regulator-always-on; + regulator-enable-ramp-delay = <300>; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1350000>; + regulator-name = "vdd_gpu"; + regulator-ramp-delay = <8000>; + vin-supply = <&vcc_sys>; + }; +}; + +&pinctrl { + pcfg_output_high: pcfg-output-high { + output-high; + }; + + emmc { + emmc_reset: emmc-reset { + rockchip,pins = <3 9 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + gmac { + phy_rst: phy-rst { + rockchip,pins = <4 8 RK_FUNC_GPIO &pcfg_output_high>; + }; + }; +}; + +&tsadc { + rockchip,hw-tshut-mode = <0>; /* tshut mode 0:CRU 1:GPIO */ + rockchip,hw-tshut-polarity = <0>; /* tshut polarity 0:LOW 1:HIGH */ + status = "okay"; +}; + +&vopb { + status = "okay"; +}; + +&vopb_mmu { + status = "okay"; +}; + +&vopl { + status = "okay"; +}; + +&vopl_mmu { + status = "okay"; +}; + +&wdt { + status = "okay"; +}; diff --git a/arch/arm/dts/rk3288-rock2-square.dts b/arch/arm/dts/rk3288-rock2-square.dts new file mode 100644 index 0000000..c5453a0 --- /dev/null +++ b/arch/arm/dts/rk3288-rock2-square.dts @@ -0,0 +1,180 @@ +/* + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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 file 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. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; +#include "rk3288-rock2-som.dtsi" + +/ { + model = "Radxa Rock 2 Square"; + compatible = "radxa,rock2-square", "rockchip,rk3288"; + + chosen { + stdout-path = "serial2:115200n8"; + }; + + ir: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio8 1 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&ir_int>; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "SPDIF"; + simple-audio-card,dai-link@1 { /* S/PDIF - S/PDIF */ + cpu { sound-dai = <&spdif>; }; + codec { sound-dai = <&spdif_out>; }; + }; + }; + + spdif_out: spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + vcc_usb_host: vcc-host-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 14 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&host_vbus_drv>; + /* Always on as the rockchip usb phy doesn't have a vbus-supply + * property + */ + regulator-always-on; + regulator-name = "vcc_host"; + }; + + vcc_sd: sdmmc-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio7 11 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_pwr>; + regulator-name = "vcc_sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_io>; + }; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + card-detect-delay = <200>; + disable-wp; /* wp not hooked up */ + num-slots = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; + vmmc-supply = <&vcc_sd>; + vqmmc-supply = <&vccio_sd>; + status = "okay"; +}; + +&gmac { + status = "ok"; +}; + +&hdmi { + ddc-i2c-bus = <&i2c5>; + status = "okay"; +}; + +&i2c0 { + hym8563@51 { + compatible = "haoyu,hym8563"; + reg = <0x51>; + #clock-cells = <0>; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + interrupt-parent = <&gpio0>; + interrupts = <4 IRQ_TYPE_EDGE_FALLING>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int>; + + }; +}; + +&i2c5 { + status = "okay"; +}; + +&pinctrl { + ir { + ir_int: ir-int { + rockchip,pins = <8 1 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + pmic { + pmic_int: pmic-int { + rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + usb { + host_vbus_drv: host-vbus-drv { + rockchip,pins = <0 14 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + sdmmc { + sdmmc_pwr: sdmmc-pwr { + rockchip,pins = <7 11 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&spdif { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&usbphy { + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +};

Add the required pre-relocation tags and SDRAM init information for U-Boot.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/dts/rk3288-rock2-square.dts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/arch/arm/dts/rk3288-rock2-square.dts b/arch/arm/dts/rk3288-rock2-square.dts index c5453a0..8d7446f 100644 --- a/arch/arm/dts/rk3288-rock2-square.dts +++ b/arch/arm/dts/rk3288-rock2-square.dts @@ -96,6 +96,7 @@ };
&sdmmc { + u-boot,dm-pre-reloc; bus-width = <4>; cap-mmc-highspeed; cap-sd-highspeed; @@ -138,6 +139,7 @@ };
&pinctrl { + u-boot,dm-pre-reloc; ir { ir_int: ir-int { rockchip,pins = <8 1 RK_FUNC_GPIO &pcfg_pull_up>; @@ -169,6 +171,8 @@
&uart2 { status = "okay"; + u-boot,dm-pre-reloc; + reg-shift = <2>; };
&usbphy { @@ -178,3 +182,20 @@ &usb_host0_ehci { status = "okay"; }; + +&dmc { + rockchip,num-channels = <2>; + rockchip,pctl-timing = <0x29a 0xc8 0x1f8 0x42 0x4e 0x4 0xea 0xa + 0x5 0x0 0xa 0x7 0x19 0x24 0xa 0x7 + 0x5 0xa 0x5 0x200 0x5 0x10 0x40 0x0 + 0x1 0x7 0x7 0x4 0xc 0x43 0x100 0x0 + 0x5 0x0>; + rockchip,phy-timing = <0x48f9aab4 0xea0910 0x1002c200 + 0xa60 0x40 0x10 0x0>; + rockchip,sdram-channel = /bits/ 8 <0x1 0xa 0x3 0x2 0x1 0x0 0xf 0xf>; + rockchip,sdram-params = <0x30B25564 0x627 3 666000000 3 9 1>; +}; + +&gpio7 { + u-boot,dm-pre-reloc; +};

This board includes an RK3288 SoC on a SOM. It can be mounted on a base-board which provides a wide range of peripherals.
So far this is verified to boot to a prompt from a microSD card. The serial console works as well as HDMI.
Thanks to Tom Cubie for sending me a board.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/dts/Makefile | 1 + arch/arm/mach-rockchip/rk3288-board-spl.c | 2 ++ arch/arm/mach-rockchip/rk3288/Kconfig | 10 ++++++ board/radxa/rock2/Kconfig | 15 +++++++++ board/radxa/rock2/MAINTAINERS | 6 ++++ board/radxa/rock2/Makefile | 7 ++++ board/radxa/rock2/rock2.c | 7 ++++ configs/rock2_defconfig | 53 +++++++++++++++++++++++++++++++ doc/README.rockchip | 7 +--- include/configs/rock2.h | 31 ++++++++++++++++++ 10 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 board/radxa/rock2/Kconfig create mode 100644 board/radxa/rock2/MAINTAINERS create mode 100644 board/radxa/rock2/Makefile create mode 100644 board/radxa/rock2/rock2.c create mode 100644 configs/rock2_defconfig create mode 100644 include/configs/rock2.h
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 03f984a..2960cd1 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -22,6 +22,7 @@ dtb-$(CONFIG_EXYNOS5) += exynos5250-arndale.dtb \ dtb-$(CONFIG_ARCH_ROCKCHIP) += \ rk3288-firefly.dtb \ rk3288-jerry.dtb \ + rk3288-rock2-square.dtb \ rk3036-sdk.dtb dtb-$(CONFIG_TEGRA) += tegra20-harmony.dtb \ tegra20-medcom-wide.dtb \ diff --git a/arch/arm/mach-rockchip/rk3288-board-spl.c b/arch/arm/mach-rockchip/rk3288-board-spl.c index 9fdd37d..6a54368 100644 --- a/arch/arm/mach-rockchip/rk3288-board-spl.c +++ b/arch/arm/mach-rockchip/rk3288-board-spl.c @@ -114,6 +114,7 @@ static void configure_l2ctlr(void) #ifdef CONFIG_SPL_MMC_SUPPORT static int configure_emmc(struct udevice *pinctrl) { +#ifndef CONFIG_TARGET_ROCK2 struct gpio_desc desc; int ret;
@@ -143,6 +144,7 @@ static int configure_emmc(struct udevice *pinctrl) debug("gpio value ret=%d\n", ret); return ret; } +#endif
return 0; } diff --git a/arch/arm/mach-rockchip/rk3288/Kconfig b/arch/arm/mach-rockchip/rk3288/Kconfig index ed89c3e..7215624 100644 --- a/arch/arm/mach-rockchip/rk3288/Kconfig +++ b/arch/arm/mach-rockchip/rk3288/Kconfig @@ -16,6 +16,14 @@ config TARGET_CHROMEBOOK_JERRY WiFi. It includes a Chrome OS EC (Cortex-M3) to provide access to the keyboard and battery functions.
+config TARGET_ROCK2 + bool "Radxa Rock 2" + help + Rock 2 is a SOM and base-board combination based on RK3288. It + includes Ethernet, HDMI, 3 USB, micro-SD, audio, SATA, WiFi and + space for a real-time-clock battery. There is also an expansion + interface which provides access to many pins. + config ROCKCHIP_FAST_SPL bool "Change the CPU to full speed in SPL" depends on TARGET_CHROMEBOOK_JERRY @@ -35,4 +43,6 @@ source "board/google/chromebook_jerry/Kconfig"
source "board/firefly/firefly-rk3288/Kconfig"
+source "board/radxa/rock2/Kconfig" + endif diff --git a/board/radxa/rock2/Kconfig b/board/radxa/rock2/Kconfig new file mode 100644 index 0000000..c2ff9e9 --- /dev/null +++ b/board/radxa/rock2/Kconfig @@ -0,0 +1,15 @@ +if TARGET_ROCK2 + +config SYS_BOARD + default "rock2" + +config SYS_VENDOR + default "radxa" + +config SYS_CONFIG_NAME + default "rock2" + +config BOARD_SPECIFIC_OPTIONS # dummy + def_bool y + +endif diff --git a/board/radxa/rock2/MAINTAINERS b/board/radxa/rock2/MAINTAINERS new file mode 100644 index 0000000..a697e68 --- /dev/null +++ b/board/radxa/rock2/MAINTAINERS @@ -0,0 +1,6 @@ +FIREFLY +M: Simon Glass sjg@chromium.org +S: Maintained +F: board/radxa/rock2 +F: include/configs/rock2.h +F: configs/rock2_defconfig diff --git a/board/radxa/rock2/Makefile b/board/radxa/rock2/Makefile new file mode 100644 index 0000000..caa305b --- /dev/null +++ b/board/radxa/rock2/Makefile @@ -0,0 +1,7 @@ +# +# (C) Copyright 2015 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += rock2.o diff --git a/board/radxa/rock2/rock2.c b/board/radxa/rock2/rock2.c new file mode 100644 index 0000000..5119e95 --- /dev/null +++ b/board/radxa/rock2/rock2.c @@ -0,0 +1,7 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> diff --git a/configs/rock2_defconfig b/configs/rock2_defconfig new file mode 100644 index 0000000..2563775 --- /dev/null +++ b/configs/rock2_defconfig @@ -0,0 +1,53 @@ +CONFIG_ARM=y +CONFIG_ARCH_ROCKCHIP=y +CONFIG_SYS_MALLOC_F_LEN=0x2000 +CONFIG_ROCKCHIP_RK3288=y +CONFIG_TARGET_ROCK2=y +CONFIG_SPL_STACK_R_ADDR=0x80000 +CONFIG_DEFAULT_DEVICE_TREE="rk3288-rock2-square" +CONFIG_SPL_STACK_R=y +CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x2000 +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y +CONFIG_SPL_OF_CONTROL=y +CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent" +CONFIG_REGMAP=y +CONFIG_SPL_REGMAP=y +CONFIG_SYSCON=y +CONFIG_SPL_SYSCON=y +# CONFIG_SPL_SIMPLE_BUS is not set +CONFIG_CLK=y +CONFIG_SPL_CLK=y +CONFIG_ROCKCHIP_GPIO=y +CONFIG_SYS_I2C_ROCKCHIP=y +CONFIG_RESET=y +CONFIG_DM_MMC=y +CONFIG_ROCKCHIP_DWMMC=y +CONFIG_PINCTRL=y +CONFIG_SPL_PINCTRL=y +# CONFIG_SPL_PINCTRL_FULL is not set +CONFIG_ROCKCHIP_PINCTRL=y +CONFIG_DM_PMIC=y +# CONFIG_SPL_PMIC_CHILDREN is not set +CONFIG_PMIC_ACT8846=y +CONFIG_DM_REGULATOR=y +CONFIG_DM_REGULATOR_FIXED=y +CONFIG_REGULATOR_ACT8846=y +CONFIG_DM_PWM=y +CONFIG_PWM_ROCKCHIP=y +CONFIG_RAM=y +CONFIG_SPL_RAM=y +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_BASE=0xff690000 +CONFIG_DEBUG_UART_CLOCK=24000000 +CONFIG_DEBUG_UART_SHIFT=2 +CONFIG_SYS_NS16550=y +CONFIG_DM_VIDEO=y +CONFIG_VIDEO_ROTATION=y +CONFIG_VIDEO_ROCKCHIP=y +CONFIG_USE_PRIVATE_LIBGCC=y +CONFIG_USE_TINY_PRINTF=y +CONFIG_CMD_DHRYSTONE=y +CONFIG_ERRNO_STR=y diff --git a/doc/README.rockchip b/doc/README.rockchip index 7be5a13..364affd 100644 --- a/doc/README.rockchip +++ b/doc/README.rockchip @@ -39,7 +39,7 @@ Building At present three RK3288 boards are supported:
- Firefly RK3288 - use firefly-rk3288 configuration - - Radxa Rock 2 - also uses firefly-rk3288 configuration + - Radxa Rock 2 - use rock2 configuration - Haier Chromebook - use chromebook_jerry configuration
one RK3036 board is support: @@ -52,11 +52,6 @@ For example:
(or you can use another cross compiler if you prefer)
-Note that the Radxa Rock 2 uses the Firefly configuration for now as -device tree files are not yet available for the Rock 2. Clearly the two -have hardware differences, so this approach will break down as more drivers -are added. -
Writing to the board with USB ============================= diff --git a/include/configs/rock2.h b/include/configs/rock2.h new file mode 100644 index 0000000..d6423e7 --- /dev/null +++ b/include/configs/rock2.h @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +#define ROCKCHIP_DEVICE_SETTINGS \ + "stdin=serial,cros-ec-keyb\0" \ + "stdout=serial,vidconsole\0" \ + "stderr=serial,vidconsole\0" + +#include <configs/rk3288_common.h> + +#define CONFIG_SPL_MMC_SUPPORT + +#define CONFIG_ENV_IS_IN_MMC +#define CONFIG_SYS_MMC_ENV_DEV 0 +/* SPL @ 32k for ~36k + * ENV @ 96k + * u-boot @ 128K + */ +#define CONFIG_ENV_OFFSET (96 * 1024) + +#define CONFIG_I2C_EDID +#define CONFIG_SYS_WHITE_ON_BLACK +#define CONFIG_CONSOLE_SCROLL_LINES 10 + +#endif

GPIO, I2C, LCD and HDMI are now implemented. We have more than one PMIC. There is an implementation to run the CPU at full speed although it does not seem to make much difference.
Update the README to cover recent developments.
Signed-off-by: Simon Glass sjg@chromium.org ---
doc/README.rockchip | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/doc/README.rockchip b/doc/README.rockchip index 364affd..e0572c8 100644 --- a/doc/README.rockchip +++ b/doc/README.rockchip @@ -14,7 +14,7 @@ many Rockchip devices [1] [2]. The current mainline support is experimental only and is not useful for anything. It should provide a base on which to build.
-So far only support for the RK3288 is provided. +So far only support for the RK3288 and RK3036 is provided.
Prerequisites @@ -22,7 +22,7 @@ Prerequisites
You will need:
- - Firefly RK3288 baord + - Firefly RK3288 board or something else with a supported RockChip SoC - Power connection to 5V using the supplied micro-USB power cable - Separate USB serial cable attached to your computer and the Firefly (connect to the micro-USB connector below the logo) @@ -40,11 +40,12 @@ At present three RK3288 boards are supported:
- Firefly RK3288 - use firefly-rk3288 configuration - Radxa Rock 2 - use rock2 configuration - - Haier Chromebook - use chromebook_jerry configuration + - Hisense Chromebook - use chromebook_jerry configuration
-one RK3036 board is support: +Two RK3036 board are supported:
- - EVB RK3036 - use evb-rk3036_defconfig configuration + - EVB RK3036 - use evb-rk3036 configuration + - Kylin - use kylin_rk3036 configuration
For example:
@@ -103,20 +104,23 @@ corresponds with this setting in U-Boot: Put this SD (or micro-SD) card into your board and reset it. You should see something like:
- U-Boot SPL 2015.07-rc1-00383-ge345740-dirty (Jun 03 2015 - 11:04:40) - - - U-Boot 2015.07-rc1-00383-ge345740-dirty (Jun 03 2015 - 11:04:40) + U-Boot 2016.01-rc2-00309-ge5bad3b-dirty (Jan 02 2016 - 23:41:59 -0700)
+ Model: Radxa Rock 2 Square DRAM: 2 GiB - MMC: - Using default environment - - In: serial@ff690000 - Out: serial@ff690000 - Err: serial@ff690000 + MMC: dwmmc@ff0f0000: 0, dwmmc@ff0c0000: 1 + *** Warning - bad CRC, using default environment + + In: serial + Out: vop@ff940000.vidconsole + Err: serial + Net: Net Initialization Skipped + No ethernet found. + Hit any key to stop autoboot: 0 =>
+If you have an HDMI cable attached you should see a video console. + For evb_rk3036 board: ./evb-rk3036/tools/mkimage -n rk3036 -T rksd -d evb-rk3036/spl/u-boot-spl.bin out && \ cat evb-rk3036/u-boot-dtb.bin >> out && \ @@ -170,13 +174,9 @@ Future work
Immediate priorities are:
-- GPIO (driver exists but is lightly tested) -- I2C (driver exists but is non-functional) - USB host - USB device -- PMIC and regulators (only ACT8846 is supported at present) -- LCD and HDMI -- Run CPU at full speed +- Run CPU at full speed (code exists but we only see ~60 DMIPS maximum) - Ethernet - NAND flash - Support for other Rockchip parts

Hi,
On 14 January 2016 at 08:57, Simon Glass sjg@chromium.org wrote:
This series adds display drivers for rockchip and enables them on jerry, firefly-rk3288 (HDMI only) and rock2 (HDMI only). It builds on the recent keyboard series.
Driver are provided for video displays (EDP and HDMI) and the VOP (video output processor).
This series also adds several new uclasses:
- PWM (Pulse-width modulation)
- Panel (used for LCD panels)
- Display (renamed from DisplayPort - a generic display with power, etc.)
- Backlight (used for LCD backlights)
Since most of this implementation is device-tree-controlled it is fairly easy to enable HDMI on the other RK3288 devices. This series adds this for Firefly-RK3288. It also includes Radxa Rock 2 as a separate board since a device tree file is now available in Linux. HDMI is enabled on that board also.
There remain quite a few things still to do for core rockchip support. Here are a few noticed while preparing this series:
- Clocks / caches are not working correctly. The 'dhry' command shows
approximately 60 DMIPS when supposedly running at 1.8GHz. Turning the cache on or off makes no difference. The platform should run at over 2000 DMIPS by my estimate. This problem is common on ARM platforms. For example I found:
beaver (Tegra) 818 DMIPS pit (Exynos) 276 DMIPS snow (Exynos) 521 DMIPS
- HDMI EDID-reading does not work on Jerry, perhaps because the clocks are
not running as they should. It seems to work fine on other boards.
This series is available at u-boot-dm/rkd-working
I know this is pretty soon, but I'd like to apply this to u-boot-rockchip in the next day or two. If there are any more comments, please send them through.
Regards, Simon
participants (1)
-
Simon Glass