video/drm: Add support for rk618

RK618 is a partner chip for Rockchip mobile application processor.

RK618 includes two RGB display input interface with double data rate.
With the internal MUX function, it can output 1080P HDMI signal to
TV and output RGB/LVDS/MIPI signal to TFT panel. In this case, RK618
can support dual panel (TV and TFT) display.

Change-Id: I566b161211e6662f73e5de2b14fb20b33e8e57ef
Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
Wyon Bi 2018-07-26 17:25:57 +08:00
parent 1a8d717c29
commit 117fdc8998
5 changed files with 393 additions and 0 deletions

View File

@ -71,6 +71,15 @@ config DRM_ROCKCHIP_RGB
Choose this option to enable support for Rockchip RGB driver.
say Y to enable its driver.
config DRM_ROCKCHIP_RK618
bool "Rockchip RK618 display bridge driver"
depends on DRM_ROCKCHIP
select DRM_ROCKCHIP_PANEL
select VIDEO_BRIDGE
help
Choose this option to enable support for Rockchip RK618 display
bridge chips driver. say Y to enable its driver.
config ROCKCHIP_DRM_TVE
bool "Rockchip TVE Support"
depends on DRM_ROCKCHIP

View File

@ -17,3 +17,4 @@ obj-$(CONFIG_DRM_ROCKCHIP_ANALOGIX_DP) += analogix_dp.o analogix_dp_reg.o
obj-$(CONFIG_DRM_ROCKCHIP_LVDS) += rockchip_lvds.o
obj-$(CONFIG_DRM_ROCKCHIP_RGB) += rockchip_rgb.o
obj-$(CONFIG_DRM_ROCKCHIP_PANEL) += rockchip_panel.o
obj-$(CONFIG_DRM_ROCKCHIP_RK618) += rk618.o rk618_lvds.o

183
drivers/video/drm/rk618.c Normal file
View File

@ -0,0 +1,183 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2008-2018 Fuzhou Rockchip Electronics Co., Ltd
*/
#include <common.h>
#include <i2c.h>
#include <errno.h>
#include <dm.h>
#include <dm/uclass.h>
#include <dm/uclass-id.h>
#include "rk618.h"
int rk618_i2c_write(struct rk618 *rk618, u16 reg, u32 val)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(rk618->dev);
struct i2c_msg msg;
u8 buf[] = {
(reg >> 0) & 0xff, (reg >> 8) & 0xff,
(val >> 0) & 0xff, (val >> 8) & 0xff,
(val >> 16) & 0xff, (val >> 24) & 0xff
};
int ret;
msg.addr = chip->chip_addr;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
ret = dm_i2c_xfer(rk618->dev, &msg, 1);
if (ret) {
dev_err(rk618->dev, "Could not execute transfer: %d\n", ret);
return ret;
}
return 0;
}
int rk618_i2c_read(struct rk618 *rk618, u16 reg, u32 *val)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(rk618->dev);
u32 data;
struct i2c_msg msg[] = {
{
.addr = chip->chip_addr,
.flags = 0,
.buf = (u8 *)&reg,
.len = 2,
}, {
.addr = chip->chip_addr,
.flags = I2C_M_RD,
.buf = (u8 *)&data,
.len = 4,
}
};
int ret;
ret = dm_i2c_xfer(rk618->dev, msg, 2);
if (ret) {
dev_err(rk618->dev, "Could not execute transfer: %d\n", ret);
return ret;
}
*val = data;
return 0;
}
void rk618_frc_dither_disable(struct rk618 *rk618)
{
rk618_i2c_write(rk618, RK618_FRC_REG, FRC_DITHER_DISABLE);
}
void rk618_frc_dither_enable(struct rk618 *rk618)
{
rk618_i2c_write(rk618, RK618_FRC_REG, FRC_DITHER_ENABLE);
}
void rk618_frc_dclk_invert(struct rk618 *rk618)
{
rk618_i2c_write(rk618, RK618_FRC_REG, FRC_DCLK_INV);
}
static int rk618_power_on(struct rk618 *rk618)
{
if (rk618->power_supply)
regulator_set_enable(rk618->power_supply, 1);
if (dm_gpio_is_valid(&rk618->enable_gpio))
dm_gpio_set_value(&rk618->enable_gpio, 1);
mdelay(2);
dm_gpio_set_value(&rk618->reset_gpio, 0);
mdelay(4);
dm_gpio_set_value(&rk618->reset_gpio, 1);
mdelay(50);
dm_gpio_set_value(&rk618->reset_gpio, 0);
return 0;
}
static void rk618_cru_init(struct rk618 *rk618)
{
rk618_i2c_write(rk618, 0x0058, 0xffff0000);
rk618_i2c_write(rk618, 0x005c, 0xffff1d1e);
rk618_i2c_write(rk618, 0x0060, 0x00000000);
rk618_i2c_write(rk618, 0x0064, 0xffff2186);
rk618_i2c_write(rk618, 0x0068, 0xffff1028);
rk618_i2c_write(rk618, 0x006c, 0xffff0641);
rk618_i2c_write(rk618, 0x0070, 0x00800000);
rk618_i2c_write(rk618, 0x0074, 0xffff1028);
rk618_i2c_write(rk618, 0x0078, 0xffff0641);
rk618_i2c_write(rk618, 0x007c, 0x00800000);
}
static int rk618_probe(struct udevice *dev)
{
struct rk618 *rk618 = dev_get_priv(dev);
int ret;
rk618->dev = dev;
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
"power-supply",
&rk618->power_supply);
if (ret && ret != -ENOENT) {
dev_err(dev, "Cannot get power supply: %d\n", ret);
return ret;
}
ret = gpio_request_by_name(dev, "enable-gpios", 0,
&rk618->enable_gpio, GPIOD_IS_OUT);
if (ret && ret != -ENOENT) {
dev_err(dev, "Cannot get enable GPIO: %d\n", ret);
return ret;
}
ret = gpio_request_by_name(dev, "reset-gpios", 0,
&rk618->reset_gpio, GPIOD_IS_OUT);
if (ret) {
dev_err(dev, "Cannot get reset GPIO: %d\n", ret);
return ret;
}
ret = clk_get_by_name(dev, "clkin", &rk618->clkin);
if (ret < 0) {
dev_err(dev, "failed to get clkin: %d\n", ret);
return ret;
}
ret = clk_set_rate(&rk618->clkin, 12000000);
if (ret < 0) {
dev_err(dev, "failed to set rate: %d\n", ret);
return ret;
}
clk_enable(&rk618->clkin);
ret = rk618_power_on(rk618);
if (ret) {
dev_err(dev, "failed to power on: %d\n", ret);
return ret;
}
rk618_cru_init(rk618);
return 0;
}
static const struct udevice_id rk618_of_match[] = {
{ .compatible = "rockchip,rk618" },
{}
};
U_BOOT_DRIVER(rk618) = {
.name = "rk618",
.id = UCLASS_I2C_GENERIC,
.of_match = rk618_of_match,
.probe = rk618_probe,
.bind = dm_scan_fdt_dev,
.priv_auto_alloc_size = sizeof(struct rk618),
};

73
drivers/video/drm/rk618.h Normal file
View File

@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2008-2018 Fuzhou Rockchip Electronics Co., Ltd
*/
#ifndef _RK618_H_
#define _RK618_H_
#include <clk.h>
#include <dm/device.h>
#include <power/regulator.h>
#include <asm/gpio.h>
#define UPDATE(v, h, l) (((v) << (l)) & GENMASK((h), (l)))
#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK(h, l) << 16))
#define RK618_FRC_REG 0x0054
#define FRC_DEN_INV HIWORD_UPDATE(1, 6, 6)
#define FRC_SYNC_INV HIWORD_UPDATE(1, 5, 5)
#define FRC_DCLK_INV HIWORD_UPDATE(1, 4, 4)
#define FRC_OUT_ZERO HIWORD_UPDATE(1, 3, 3)
#define FRC_OUT_MODE_RGB666 HIWORD_UPDATE(1, 2, 2)
#define FRC_OUT_MODE_RGB888 HIWORD_UPDATE(0, 2, 2)
#define FRC_DITHER_MODE_HI_FRC HIWORD_UPDATE(1, 1, 1)
#define FRC_DITHER_MODE_FRC HIWORD_UPDATE(0, 1, 1)
#define FRC_DITHER_ENABLE HIWORD_UPDATE(1, 0, 0)
#define FRC_DITHER_DISABLE HIWORD_UPDATE(0, 0, 0)
#define RK618_LVDS_CON 0x0084
#define LVDS_CON_START_PHASE(x) HIWORD_UPDATE(x, 14, 14)
#define LVDS_DCLK_INV HIWORD_UPDATE(1, 13, 13)
#define LVDS_CON_CHADS_10PF HIWORD_UPDATE(3, 12, 11)
#define LVDS_CON_CHADS_5PF HIWORD_UPDATE(2, 12, 11)
#define LVDS_CON_CHADS_7PF HIWORD_UPDATE(1, 12, 11)
#define LVDS_CON_CHADS_3PF HIWORD_UPDATE(0, 12, 11)
#define LVDS_CON_CHA1TTL_ENABLE HIWORD_UPDATE(1, 10, 10)
#define LVDS_CON_CHA1TTL_DISABLE HIWORD_UPDATE(0, 10, 10)
#define LVDS_CON_CHA0TTL_ENABLE HIWORD_UPDATE(1, 9, 9)
#define LVDS_CON_CHA0TTL_DISABLE HIWORD_UPDATE(0, 9, 9)
#define LVDS_CON_CHA1_POWER_UP HIWORD_UPDATE(1, 8, 8)
#define LVDS_CON_CHA1_POWER_DOWN HIWORD_UPDATE(0, 8, 8)
#define LVDS_CON_CHA0_POWER_UP HIWORD_UPDATE(1, 7, 7)
#define LVDS_CON_CHA0_POWER_DOWN HIWORD_UPDATE(0, 7, 7)
#define LVDS_CON_CBG_POWER_UP HIWORD_UPDATE(1, 6, 6)
#define LVDS_CON_CBG_POWER_DOWN HIWORD_UPDATE(0, 6, 6)
#define LVDS_CON_PLL_POWER_DOWN HIWORD_UPDATE(1, 5, 5)
#define LVDS_CON_PLL_POWER_UP HIWORD_UPDATE(0, 5, 5)
#define LVDS_CON_START_SEL_EVEN_PIXEL HIWORD_UPDATE(1, 4, 4)
#define LVDS_CON_START_SEL_ODD_PIXEL HIWORD_UPDATE(0, 4, 4)
#define LVDS_CON_CHASEL_DOUBLE_CHANNEL HIWORD_UPDATE(1, 3, 3)
#define LVDS_CON_CHASEL_SINGLE_CHANNEL HIWORD_UPDATE(0, 3, 3)
#define LVDS_CON_MSBSEL_D7 HIWORD_UPDATE(1, 2, 2)
#define LVDS_CON_MSBSEL_D0 HIWORD_UPDATE(0, 2, 2)
#define LVDS_CON_SELECT(x) HIWORD_UPDATE(x, 1, 0)
#define LVDS_CON_SELECT_6BIT_MODE HIWORD_UPDATE(3, 1, 0)
#define LVDS_CON_SELECT_8BIT_MODE_3 HIWORD_UPDATE(2, 1, 0)
#define LVDS_CON_SELECT_8BIT_MODE_2 HIWORD_UPDATE(1, 1, 0)
#define LVDS_CON_SELECT_8BIT_MODE_1 HIWORD_UPDATE(0, 1, 0)
struct rk618 {
struct udevice *dev;
struct udevice *power_supply;
struct gpio_desc enable_gpio;
struct gpio_desc reset_gpio;
struct clk clkin;
};
int rk618_i2c_write(struct rk618 *rk618, u16 reg, u32 val);
int rk618_i2c_read(struct rk618 *rk618, u16 reg, u32 *val);
void rk618_frc_dither_disable(struct rk618 *rk618);
void rk618_frc_dither_enable(struct rk618 *rk618);
void rk618_frc_dclk_invert(struct rk618 *rk618);
#endif

View File

@ -0,0 +1,127 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2008-2018 Fuzhou Rockchip Electronics Co., Ltd
*/
#include <common.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <errno.h>
#include <i2c.h>
#include <video_bridge.h>
#include <linux/media-bus-format.h>
#include "rockchip_display.h"
#include "rockchip_bridge.h"
#include "rockchip_panel.h"
#include "rk618.h"
enum {
LVDS_8BIT_MODE_FORMAT_1,
LVDS_8BIT_MODE_FORMAT_2,
LVDS_8BIT_MODE_FORMAT_3,
LVDS_6BIT_MODE,
};
struct rk618_lvds_priv {
struct udevice *dev;
struct rk618 *parent;
bool dual_channel;
};
static int lvds_write(struct rk618_lvds_priv *priv, u16 reg, u32 val)
{
return rk618_i2c_write(priv->parent, reg, val);
}
static void rk618_lvds_bridge_enable(struct rockchip_bridge *bridge)
{
struct rk618_lvds_priv *priv = dev_get_priv(bridge->dev);
struct rockchip_panel *panel = state_get_panel(bridge->state);
u32 value, format;
rk618_frc_dclk_invert(priv->parent);
switch (panel->bus_format) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* jeida-18 */
format = LVDS_6BIT_MODE;
break;
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* jeida-24 */
format = LVDS_8BIT_MODE_FORMAT_2;
break;
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* vesa-24 */
format = LVDS_8BIT_MODE_FORMAT_1;
break;
default:
format = LVDS_8BIT_MODE_FORMAT_3;
break;
}
value = LVDS_CON_CHA0TTL_DISABLE | LVDS_CON_CHA1TTL_DISABLE |
LVDS_CON_CHA0_POWER_UP | LVDS_CON_CBG_POWER_UP |
LVDS_CON_PLL_POWER_UP | LVDS_CON_SELECT(format);
if (priv->dual_channel)
value |= LVDS_CON_CHA1_POWER_UP | LVDS_DCLK_INV |
LVDS_CON_CHASEL_DOUBLE_CHANNEL;
else
value |= LVDS_CON_CHA1_POWER_DOWN |
LVDS_CON_CHASEL_SINGLE_CHANNEL;
lvds_write(priv, RK618_LVDS_CON, value);
}
static void rk618_lvds_bridge_disable(struct rockchip_bridge *bridge)
{
struct rk618_lvds_priv *priv = dev_get_priv(bridge->dev);
lvds_write(priv, RK618_LVDS_CON,
LVDS_CON_CHA0_POWER_DOWN | LVDS_CON_CHA1_POWER_DOWN |
LVDS_CON_CBG_POWER_DOWN | LVDS_CON_PLL_POWER_DOWN);
}
static const struct rockchip_bridge_funcs rk618_lvds_bridge_funcs = {
.enable = rk618_lvds_bridge_enable,
.disable = rk618_lvds_bridge_disable,
};
static int rk618_lvds_probe(struct udevice *dev)
{
struct rk618_lvds_priv *priv = dev_get_priv(dev);
struct rockchip_bridge *bridge =
(struct rockchip_bridge *)dev_get_driver_data(dev);
int ret;
priv->dev = dev;
priv->parent = dev_get_priv(dev->parent);
priv->dual_channel = dev_read_bool(dev, "dual-channel");
ret = device_probe(dev->parent);
if (ret)
return ret;
bridge->dev = dev;
return 0;
}
static struct rockchip_bridge rk618_lvds_driver_data = {
.funcs = &rk618_lvds_bridge_funcs,
};
static const struct udevice_id rk618_lvds_ids[] = {
{
.compatible = "rockchip,rk618-lvds",
.data = (ulong)&rk618_lvds_driver_data,
},
{ }
};
U_BOOT_DRIVER(rk618_lvds) = {
.name = "rk618_lvds",
.id = UCLASS_VIDEO_BRIDGE,
.of_match = rk618_lvds_ids,
.probe = rk618_lvds_probe,
.priv_auto_alloc_size = sizeof(struct rk618_lvds_priv),
};