mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-01 17:10:11 +00:00
135aa95002
The following changes are made to the clock API: * The concept of "clocks" and "peripheral clocks" are unified; each clock provider now implements a single set of clocks. This provides a simpler conceptual interface to clients, and better aligns with device tree clock bindings. * Clocks are now identified with a single "struct clk", rather than requiring clients to store the clock provider device and clock identity values separately. For simple clock consumers, this isolates clients from internal details of the clock API. * clk.h is split so it only contains the client/consumer API, whereas clk-uclass.h contains the provider API. This aligns with the recently added reset and mailbox APIs. * clk_ops .of_xlate(), .request(), and .free() are added so providers can customize these operations if needed. This also aligns with the recently added reset and mailbox APIs. * clk_disable() is added. * All users of the current clock APIs are updated. * Sandbox clock tests are updated to exercise clock lookup via DT, and clock enable/disable. * rkclk_get_clk() is removed and replaced with standard APIs. Buildman shows no clock-related errors for any board for which buildman can download a toolchain. test/py passes for sandbox (which invokes the dm clk test amongst others). Signed-off-by: Stephen Warren <swarren@nvidia.com> Acked-by: Simon Glass <sjg@chromium.org>
253 lines
7 KiB
C
253 lines
7 KiB
C
/*
|
|
* Copyright 2016 Rockchip Inc.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
#include <common.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/lvds_rk3288.h>
|
|
#include <asm/arch/grf_rk3288.h>
|
|
#include <dt-bindings/clock/rk3288-cru.h>
|
|
#include <dt-bindings/video/rk3288.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
/**
|
|
* struct rk_lvds_priv - private rockchip lvds display driver info
|
|
*
|
|
* @reg: LVDS register address
|
|
* @grf: GRF register
|
|
* @panel: Panel device that is used in driver
|
|
*
|
|
* @output: Output mode, decided single or double channel,
|
|
* LVDS or LVTLL
|
|
* @format: Data format that RGB data will packing as
|
|
*/
|
|
struct rk_lvds_priv {
|
|
void __iomem *regs;
|
|
struct rk3288_grf *grf;
|
|
struct udevice *panel;
|
|
|
|
int output;
|
|
int format;
|
|
};
|
|
|
|
static inline void lvds_writel(struct rk_lvds_priv *lvds, u32 offset, u32 val)
|
|
{
|
|
writel(val, lvds->regs + offset);
|
|
|
|
writel(val, lvds->regs + offset + 0x100);
|
|
}
|
|
|
|
int rk_lvds_enable(struct udevice *dev, int panel_bpp,
|
|
const struct display_timing *edid)
|
|
{
|
|
struct rk_lvds_priv *priv = dev_get_priv(dev);
|
|
struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
|
|
int ret = 0;
|
|
unsigned int val = 0;
|
|
|
|
ret = panel_enable_backlight(priv->panel);
|
|
if (ret) {
|
|
debug("%s: backlight error: %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Select the video source */
|
|
if (uc_plat->source_id)
|
|
val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
|
|
(RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
|
|
else
|
|
val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
|
|
rk_setreg(&priv->grf->soc_con6, val);
|
|
|
|
/* Select data transfer format */
|
|
val = priv->format;
|
|
if (priv->output == LVDS_OUTPUT_DUAL)
|
|
val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
|
|
else if (priv->output == LVDS_OUTPUT_SINGLE)
|
|
val |= LVDS_CH0_EN;
|
|
else if (priv->output == LVDS_OUTPUT_RGB)
|
|
val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
|
|
val |= (0xffff << 16);
|
|
rk_setreg(&priv->grf->soc_con7, val);
|
|
|
|
/* Enable LVDS PHY */
|
|
if (priv->output == LVDS_OUTPUT_RGB) {
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG0,
|
|
RK3288_LVDS_CH0_REG0_TTL_EN |
|
|
RK3288_LVDS_CH0_REG0_LANECK_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE4_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE3_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE2_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE1_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE0_EN);
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG2,
|
|
RK3288_LVDS_PLL_FBDIV_REG2(0x46));
|
|
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG3,
|
|
RK3288_LVDS_PLL_FBDIV_REG3(0x46));
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG4,
|
|
RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
|
|
RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
|
|
RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
|
|
RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
|
|
RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
|
|
RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG5,
|
|
RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
|
|
RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
|
|
RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
|
|
RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
|
|
RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
|
|
RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REGD,
|
|
RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG20,
|
|
RK3288_LVDS_CH0_REG20_LSB);
|
|
} else {
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG0,
|
|
RK3288_LVDS_CH0_REG0_LVDS_EN |
|
|
RK3288_LVDS_CH0_REG0_LANECK_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE4_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE3_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE2_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE1_EN |
|
|
RK3288_LVDS_CH0_REG0_LANE0_EN);
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG1,
|
|
RK3288_LVDS_CH0_REG1_LANECK_BIAS |
|
|
RK3288_LVDS_CH0_REG1_LANE4_BIAS |
|
|
RK3288_LVDS_CH0_REG1_LANE3_BIAS |
|
|
RK3288_LVDS_CH0_REG1_LANE2_BIAS |
|
|
RK3288_LVDS_CH0_REG1_LANE1_BIAS |
|
|
RK3288_LVDS_CH0_REG1_LANE0_BIAS);
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG2,
|
|
RK3288_LVDS_CH0_REG2_RESERVE_ON |
|
|
RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
|
|
RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
|
|
RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
|
|
RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
|
|
RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
|
|
RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
|
|
RK3288_LVDS_PLL_FBDIV_REG2(0x46));
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG3,
|
|
RK3288_LVDS_PLL_FBDIV_REG3(0x46));
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG4, 0x00);
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG5, 0x00);
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REGD,
|
|
RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
|
|
lvds_writel(priv, RK3288_LVDS_CH0_REG20,
|
|
RK3288_LVDS_CH0_REG20_LSB);
|
|
}
|
|
|
|
/* Power on */
|
|
writel(RK3288_LVDS_CFG_REGC_PLL_ENABLE,
|
|
priv->regs + RK3288_LVDS_CFG_REGC);
|
|
|
|
writel(RK3288_LVDS_CFG_REG21_TX_ENABLE,
|
|
priv->regs + RK3288_LVDS_CFG_REG21);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rk_lvds_read_timing(struct udevice *dev, struct display_timing *timing)
|
|
{
|
|
if (fdtdec_decode_display_timing
|
|
(gd->fdt_blob, dev->of_offset, 0, timing)) {
|
|
debug("%s: Failed to decode display timing\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk_lvds_ofdata_to_platdata(struct udevice *dev)
|
|
{
|
|
struct rk_lvds_priv *priv = dev_get_priv(dev);
|
|
const void *blob = gd->fdt_blob;
|
|
int node = dev->of_offset;
|
|
int ret;
|
|
priv->regs = (void *)dev_get_addr(dev);
|
|
priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
|
|
|
|
ret = fdtdec_get_int(blob, node, "rockchip,output", -1);
|
|
if (ret != -1) {
|
|
priv->output = ret;
|
|
debug("LVDS output : %d\n", ret);
|
|
} else {
|
|
/* default set it as output rgb */
|
|
priv->output = LVDS_OUTPUT_RGB;
|
|
}
|
|
|
|
ret = fdtdec_get_int(blob, node, "rockchip,data-mapping", -1);
|
|
if (ret != -1) {
|
|
priv->format = ret;
|
|
debug("LVDS data-mapping : %d\n", ret);
|
|
} else {
|
|
/* default set it as format jeida */
|
|
priv->format = LVDS_FORMAT_JEIDA;
|
|
}
|
|
|
|
ret = fdtdec_get_int(blob, node, "rockchip,data-width", -1);
|
|
if (ret != -1) {
|
|
debug("LVDS data-width : %d\n", ret);
|
|
if (ret == 24) {
|
|
priv->format |= LVDS_24BIT;
|
|
} else if (ret == 18) {
|
|
priv->format |= LVDS_18BIT;
|
|
} else {
|
|
debug("rockchip-lvds unsupport data-width[%d]\n", ret);
|
|
ret = -EINVAL;
|
|
return ret;
|
|
}
|
|
} else {
|
|
priv->format |= LVDS_24BIT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rk_lvds_probe(struct udevice *dev)
|
|
{
|
|
struct rk_lvds_priv *priv = dev_get_priv(dev);
|
|
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;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dm_display_ops lvds_rockchip_ops = {
|
|
.read_timing = rk_lvds_read_timing,
|
|
.enable = rk_lvds_enable,
|
|
};
|
|
|
|
static const struct udevice_id rockchip_lvds_ids[] = {
|
|
{.compatible = "rockchip,rk3288-lvds"},
|
|
{}
|
|
};
|
|
|
|
U_BOOT_DRIVER(lvds_rockchip) = {
|
|
.name = "lvds_rockchip",
|
|
.id = UCLASS_DISPLAY,
|
|
.of_match = rockchip_lvds_ids,
|
|
.ops = &lvds_rockchip_ops,
|
|
.ofdata_to_platdata = rk_lvds_ofdata_to_platdata,
|
|
.probe = rk_lvds_probe,
|
|
.priv_auto_alloc_size = sizeof(struct rk_lvds_priv),
|
|
};
|