X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fvideo%2Frockchip%2Frk_vop.c;h=faf4f24db04f00fd0c879e12a415cba6bd184a6c;hb=6df07d854b9ee81d31fafcd83724bed7fb1fd6d7;hp=db09d9a41df76679aa2bcd1588231597fe7adbb6;hpb=e4a94ce4ac77396b181663c0493c50bc2d5b9143;p=oweals%2Fu-boot.git diff --git a/drivers/video/rockchip/rk_vop.c b/drivers/video/rockchip/rk_vop.c index db09d9a41d..faf4f24db0 100644 --- a/drivers/video/rockchip/rk_vop.c +++ b/drivers/video/rockchip/rk_vop.c @@ -1,8 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2015 Google, Inc * Copyright 2014 Rockchip Inc. - * - * SPDX-License-Identifier: GPL-2.0+ */ #include @@ -17,25 +16,25 @@ #include #include #include -#include -#include #include -#include #include #include #include -#include #include +#include "rk_vop.h" DECLARE_GLOBAL_DATA_PTR; -struct rk_vop_priv { - struct rk3288_vop *regs; - struct rk3288_grf *grf; +enum vop_pol { + HSYNC_POSITIVE = 0, + VSYNC_POSITIVE = 1, + DEN_NEGATIVE = 2, + DCLK_INVERT = 3 }; -void rkvop_enable(struct rk3288_vop *regs, ulong fbbase, - int fb_bits_per_pixel, const struct display_timing *edid) +static 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; @@ -90,50 +89,86 @@ void rkvop_enable(struct rk3288_vop *regs, ulong fbbase, 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) +static void rkvop_set_pin_polarity(struct udevice *dev, + enum vop_modes mode, u32 polarity) { - 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; - int mode_flags; + struct rkvop_driverdata *ops = + (struct rkvop_driverdata *)dev_get_driver_data(dev); + + if (ops->set_pin_polarity) + ops->set_pin_polarity(dev, mode, polarity); +} + +static void rkvop_enable_output(struct udevice *dev, enum vop_modes mode) +{ + struct rk_vop_priv *priv = dev_get_priv(dev); + struct rk3288_vop *regs = priv->regs; + + /* remove from standby */ + clrbits_le32(®s->sys_ctrl, V_STANDBY_EN(1)); 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; + case VOP_MODE_LVDS: clrsetbits_le32(®s->sys_ctrl, M_ALL_OUT_EN, V_RGB_OUT_EN(1)); break; + + case VOP_MODE_MIPI: + clrsetbits_le32(®s->sys_ctrl, M_ALL_OUT_EN, + V_MIPI_OUT_EN(1)); + break; + + default: + debug("%s: unsupported output mode %x\n", __func__, mode); } +} - if (mode == VOP_MODE_HDMI || mode == VOP_MODE_EDP) - /* RGBaaa */ - mode_flags = 15; - else - /* RGB888 */ - mode_flags = 0; +static void rkvop_mode_set(struct udevice *dev, + const struct display_timing *edid, + enum vop_modes mode) +{ + struct rk_vop_priv *priv = dev_get_priv(dev); + struct rk3288_vop *regs = priv->regs; + struct rkvop_driverdata *data = + (struct rkvop_driverdata *)dev_get_driver_data(dev); + + 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; + int mode_flags; + u32 pin_polarity; + + pin_polarity = BIT(DCLK_INVERT); + if (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH) + pin_polarity |= BIT(HSYNC_POSITIVE); + if (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH) + pin_polarity |= BIT(VSYNC_POSITIVE); + + rkvop_set_pin_polarity(dev, mode, pin_polarity); + rkvop_enable_output(dev, mode); - flags = V_DSP_OUT_MODE(mode_flags) | - V_DSP_HSYNC_POL(!!(edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)) | - V_DSP_VSYNC_POL(!!(edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)); + mode_flags = 0; /* RGB888 */ + if ((data->features & VOP_FEATURE_OUTPUT_10BIT) && + (mode == VOP_MODE_HDMI || mode == VOP_MODE_EDP)) + mode_flags = 15; /* RGBaaa */ - clrsetbits_le32(®s->dsp_ctrl0, - M_DSP_OUT_MODE | M_DSP_VSYNC_POL | M_DSP_HSYNC_POL, - flags); + clrsetbits_le32(®s->dsp_ctrl0, M_DSP_OUT_MODE, + V_DSP_OUT_MODE(mode_flags)); writel(V_HSYNC(hsync_len) | V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch), @@ -178,49 +213,79 @@ void rkvop_mode_set(struct rk3288_vop *regs, * * @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) +static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode 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; + int ret; + u32 remote_phandle; struct display_plat *disp_uc_plat; - struct udevice *clk; + struct clk clk; + enum video_log2_bpp l2bpp; + ofnode remote; - vop_id = fdtdec_get_int(blob, ep_node, "reg", -1); + debug("%s(%s, %lu, %s)\n", __func__, + dev_read_name(dev), fbbase, ofnode_get_name(ep_node)); + + vop_id = ofnode_read_s32_default(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); + ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle); + if (ret) + return ret; - 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); + remote = ofnode_get_by_phandle(remote_phandle); + if (!ofnode_valid(remote)) return -EINVAL; - } + remote_vop_id = ofnode_read_u32_default(remote, "reg", -1); + debug("remote vop_id=%d\n", remote_vop_id); - 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; - } + /* + * The remote-endpoint references into a subnode of the encoder + * (i.e. HDMI, MIPI, etc.) with the DTS looking something like + * the following (assume 'hdmi_in_vopl' to be referenced): + * + * hdmi: hdmi@ff940000 { + * ports { + * hdmi_in: port { + * hdmi_in_vopb: endpoint@0 { ... }; + * hdmi_in_vopl: endpoint@1 { ... }; + * } + * } + * } + * + * The original code had 3 steps of "walking the parent", but + * a much better (as in: less likely to break if the DTS + * changes) way of doing this is to "find the enclosing device + * of UCLASS_DISPLAY". + */ + while (ofnode_valid(remote)) { + remote = ofnode_get_parent(remote); + if (!ofnode_valid(remote)) { + debug("%s(%s): no UCLASS_DISPLAY for remote-endpoint\n", + __func__, dev_read_name(dev)); + return -EINVAL; + } + + uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp); + if (disp) + break; + }; disp_uc_plat = dev_get_uclass_platdata(disp); debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat); + if (display_in_use(disp)) { + debug(" - device in use\n"); + return -EBUSY; + } + disp_uc_plat->source_id = remote_vop_id; disp_uc_plat->src_dev = dev; @@ -237,18 +302,29 @@ int rk_display_init(struct udevice *dev, ulong fbbase, return ret; } - ret = rkclk_get_clk(CLK_NEW, &clk); - if (!ret) { - ret = clk_set_periph_rate(clk, DCLK_VOP0 + remote_vop_id, - timing.pixelclock.typ); - } - if (ret) { + ret = clk_get_by_index(dev, 1, &clk); + if (!ret) + ret = clk_set_rate(&clk, timing.pixelclock.typ); + if (IS_ERR_VALUE(ret)) { debug("%s: Failed to set pixel clock: ret=%d\n", __func__, ret); return ret; } - rkvop_mode_set(regs, &timing, vop_id); + /* Set bitwidth for vop display according to vop mode */ + switch (vop_id) { + case VOP_MODE_EDP: + case VOP_MODE_LVDS: + l2bpp = VIDEO_BPP16; + break; + case VOP_MODE_HDMI: + case VOP_MODE_MIPI: + l2bpp = VIDEO_BPP32; + break; + default: + l2bpp = VIDEO_BPP16; + } + rkvop_mode_set(dev, &timing, vop_id); rkvop_enable(regs, fbbase, 1 << l2bpp, &timing); ret = display_enable(disp, 1 << l2bpp, &timing); @@ -263,64 +339,55 @@ int rk_display_init(struct udevice *dev, ulong fbbase, return 0; } -static int rk_vop_probe(struct udevice *dev) +void rk_vop_probe_regulators(struct udevice *dev, + const char * const *names, int cnt) +{ + int i, ret; + const char *name; + struct udevice *reg; + + for (i = 0; i < cnt; ++i) { + name = names[i]; + debug("%s: probing regulator '%s'\n", dev->name, name); + + ret = regulator_autoset_by_name(name, ®); + if (!ret) + ret = regulator_set_enable(reg, true); + } +} + +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; + int ret = 0; + ofnode 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__); + priv->regs = (struct rk3288_vop *)dev_read_addr(dev); /* * Try all the ports until we find one that works. In practice this * tries EDP first if available, then HDMI. + * + * Note that rockchip_vop_set_clk() always uses NPLL as the source + * clock so it is currently not possible to use more than one display + * device simultaneously. */ - port = fdt_subnode_offset(blob, dev->of_offset, "port"); - if (port < 0) + port = dev_read_subnode(dev, "port"); + if (!ofnode_valid(port)) { + debug("%s(%s): 'port' subnode not found\n", + __func__, dev_read_name(dev)); 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); + } + + for (node = ofnode_first_subnode(port); + ofnode_valid(node); + node = dev_read_next_subnode(node)) { + ret = rk_display_init(dev, plat->base, node); if (ret) debug("Device failed: ret=%d\n", ret); if (!ret) @@ -331,29 +398,12 @@ static int rk_vop_probe(struct udevice *dev) return ret; } -static int rk_vop_bind(struct udevice *dev) +int rk_vop_bind(struct udevice *dev) { struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); - plat->size = 1920 * 1080 * 2; + plat->size = 4 * (CONFIG_VIDEO_ROCKCHIP_MAX_XRES * + CONFIG_VIDEO_ROCKCHIP_MAX_YRES); 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), -};