From 83f1c1d9ac55279ee325137a0a67e23cbe434cd3 Mon Sep 17 00:00:00 2001 From: "algea.cao" Date: Thu, 27 Jul 2017 10:19:50 +0800 Subject: [PATCH] drm/bridge: support rk1000 tv encoder RK1000 is a digital-analog mixed chip which has tve output function. RK1000's registers can be written and read through I2C interaface. Because RK1000's I2C need dclk and mclk, RK1000 TVE should be registered after RK1000 CORE. RBG signal output is controlled by LVDS, so RK1000 should be registered as connector and attach LVDS encoder. Change-Id: I65b40826bd1dbf07d4fa94ecdf8c75005008731f Signed-off-by: Algea Cao --- .../bindings/display/bridge/rk1000.txt | 44 ++ drivers/gpu/drm/bridge/Kconfig | 8 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/rk1000.c | 424 ++++++++++++++++++ 4 files changed, 477 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/rk1000.txt create mode 100644 drivers/gpu/drm/bridge/rk1000.c diff --git a/Documentation/devicetree/bindings/display/bridge/rk1000.txt b/Documentation/devicetree/bindings/display/bridge/rk1000.txt new file mode 100644 index 000000000000..28f413e9f776 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/rk1000.txt @@ -0,0 +1,44 @@ +Rockchip RK1000 TVEncoder +------------------------------- + +The RK1000-TVE are RK1000 TV Encoder register block. +The chip is connected to an i2c bus. + +Required properties: + +- compatible: should be "rockchip,rk1000-tve" +- reg: I2C slave address +- rockchip,data-width: should be <18> or <24> +- rockchip,output: This describes the output face +- rockchip,ctl: phandle to the rk1000 core controller + + +Required node: + +The rk1000 tve has one video port. its connection is modeled using the OF +graph binding specified in Documentation/devicetree/bindings/graph.txt. + +- Video port 0 for LVDS input + + +Example +------- + + rk1000-tve@42 { + status = "okay"; + compatible = "rockchip,rk1000-tve"; + reg = <0x42>; + rockchip,data-width = <24>; + rockchip,output = "rgb"; + rockchip,ctl = <&rk1000_ctl>; + ports { + #address-cells = <1>; + #size-cells = <0>; + tve_in: port@0 { + reg = <0>; + tve_in_lvds: endpoint { + remote-endpoint = <&lvds_out_tve>; + }; + }; + }; + }; diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index ad65b3bd4e43..df2dcc157f5a 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -48,6 +48,14 @@ config DRM_PARADE_PS8622 ---help--- Parade eDP-LVDS bridge chip driver. +config DRM_RK1000 + tristate "ROCKCHIP TVE bridge" + depends on OF + select DRM_KMS_HELPER + select MFD_RK1000 + ---help--- + ROCKCHIP TVE bridge chip RK1000 driver. + source "drivers/gpu/drm/bridge/analogix/Kconfig" endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index dea158758ea5..e582bbbb808b 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ +obj-$(CONFIG_DRM_RK1000) += rk1000.o \ No newline at end of file diff --git a/drivers/gpu/drm/bridge/rk1000.c b/drivers/gpu/drm/bridge/rk1000.c new file mode 100644 index 000000000000..1169cab2abfc --- /dev/null +++ b/drivers/gpu/drm/bridge/rk1000.c @@ -0,0 +1,424 @@ +/* + * Driver for rockchip rk1000 tv encoder + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co.Ltd + * Author: + * Algea Cao + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TVE_POWCR 0x03 +#define TVE_OFF 0X07 +#define TVE_ON 0x03 + +static const struct drm_display_mode rk1000_cvbs_mode[2] = { + { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER | + DRM_MODE_TYPE_PREFERRED, 27000, 720, 732, + 738, 864, 0, 576, 582, 588, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + .vrefresh = 50, 0, }, + { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, + 742, 858, 0, 480, 486, 492, 529, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + .vrefresh = 60, 0, }, +}; + +struct rk1000_tve { + struct device *dev; + struct i2c_client *client; + struct drm_connector connector; + struct drm_bridge bridge; + struct drm_encoder *encoder; + int mode; + struct regmap *ctlmap; + struct regmap *tvemap; +}; + +enum { + CVBS_NTSC = 0, + CVBS_PAL, +}; + +static const u8 pal_tve_regs[] = {0x06, 0x00, 0x00, 0x03, 0x00, 0x00}; +static const u8 pal_ctl_regs[] = {0x41, 0x01}; + +static const u8 ntsc_tve_regs[] = {0x00, 0x00, 0x00, 0x03, 0x00, 0x00}; +static const u8 ntsc_ctl_regs[] = {0x43, 0x01}; + +static const struct regmap_range rk1000_tve_volatile_ranges[] = { + { .range_min = 0x00, .range_max = 0x05 }, +}; + +static const struct regmap_access_table rk1000_tve_reg_table = { + .yes_ranges = rk1000_tve_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(rk1000_tve_volatile_ranges), +}; + +static const struct regmap_config rk1000_tve_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &rk1000_tve_reg_table, +}; + +static const struct regmap_range rk1000_ctl_volatile_ranges[] = { + { .range_min = 0x03, .range_max = 0x04 }, +}; + +static const struct regmap_access_table rk1000_ctl_reg_table = { + .yes_ranges = rk1000_ctl_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(rk1000_ctl_volatile_ranges), +}; + +static const struct regmap_config rk1000_ctl_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &rk1000_ctl_reg_table, +}; + +static struct rk1000_tve *bridge_to_rk1000(struct drm_bridge *bridge) +{ + return container_of(bridge, struct rk1000_tve, bridge); +} + +static struct rk1000_tve *connector_to_rk1000(struct drm_connector *connector) +{ + return container_of(connector, struct rk1000_tve, connector); +} + +static int rk1000_tv_write_block(struct rk1000_tve *rk1000, + u8 reg, const u8 *buf, u8 len) +{ + int i, ret; + + for (i = 0; i < len; i++) { + ret = regmap_write(rk1000->tvemap, reg + i, buf[i]); + if (ret) + break; + } + + return ret; +} + +static int rk1000_control_write_block(struct rk1000_tve *rk1000, + u8 reg, const u8 *buf, u8 len) +{ + int i, ret; + + for (i = 0; i < len; i++) { + ret = regmap_write(rk1000->ctlmap, reg + i, buf[i]); + if (ret) + break; + } + + return ret; +} + +static int rk1000_mode_set(struct rk1000_tve *rk1000) +{ + int ret; + const u8 *tv_regs, *control_regs; + + switch (rk1000->mode) { + case CVBS_PAL: + dev_dbg(rk1000->dev, "rk1000 PAL\n"); + tv_regs = pal_tve_regs; + control_regs = pal_ctl_regs; + break; + case CVBS_NTSC: + dev_dbg(rk1000->dev, "rk1000 NTSC\n"); + tv_regs = ntsc_tve_regs; + control_regs = ntsc_ctl_regs; + break; + default: + dev_dbg(rk1000->dev, "mode select err\n"); + return -EINVAL; + } + + ret = rk1000_tv_write_block(rk1000, 0, tv_regs, 6); + if (ret) { + dev_err(rk1000->dev, "rk1000_tv_write_block err!\n"); + return ret; + } + + ret = rk1000_control_write_block(rk1000, 0x03, control_regs, 2); + if (ret < 0) { + dev_err(rk1000->dev, "rk1000 control write block err\n"); + return ret; + } + + return ret; +} + +static int rk1000_tve_disable(struct rk1000_tve *rk1000) +{ + int ret; + u8 val; + + val = TVE_OFF; + ret = rk1000_tv_write_block(rk1000, TVE_POWCR, &val, 1); + + return ret; +} + +static int rk1000_tve_enable(struct rk1000_tve *rk1000) +{ + int ret; + u8 val; + + dev_dbg(rk1000->dev, "%s\n", __func__); + ret = rk1000_mode_set(rk1000); + if (ret) + return ret; + + val = TVE_ON; + ret = rk1000_tv_write_block(rk1000, TVE_POWCR, &val, 1); + if (ret) + return ret; + + return 0; +} + +static enum drm_mode_status +rk1000_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static int +rk1000_get_modes(struct drm_connector *connector) +{ + int count; + struct rk1000_tve *rk1000 = connector_to_rk1000(connector); + + for (count = 0; count < ARRAY_SIZE(rk1000_cvbs_mode); count++) { + struct drm_display_mode *mode_ptr; + + mode_ptr = drm_mode_duplicate(connector->dev, + &rk1000_cvbs_mode[count]); + if (!mode_ptr) { + dev_err(rk1000->dev, "mode duplicate failed\n"); + return -ENOMEM; + } + drm_mode_probed_add(connector, mode_ptr); + } + + return count; +} + +static enum drm_connector_status +rk1000_connector_detect(struct drm_connector *connector, + bool force) +{ + return connector_status_connected; +} + +static struct drm_encoder *rk1000_best_encoder(struct drm_connector *connector) +{ + struct rk1000_tve *rk1000 = connector_to_rk1000(connector); + + return rk1000->encoder; +} + +static +const struct drm_connector_helper_funcs rk1000_connector_helper_funcs = { + .get_modes = rk1000_get_modes, + .mode_valid = rk1000_mode_valid, + .best_encoder = rk1000_best_encoder, +}; + +static const struct drm_connector_funcs rk1000_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = rk1000_connector_detect, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static void +rk1000_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct rk1000_tve *rk1000; + + dev_dbg(rk1000->dev, "encoder mode set:%s\n", adjusted_mode->name); + + rk1000 = bridge_to_rk1000(bridge); + if (adjusted_mode->vdisplay == 576) + rk1000->mode = CVBS_PAL; + else + rk1000->mode = CVBS_NTSC; +} + +static void rk1000_bridge_enable(struct drm_bridge *bridge) +{ + int ret; + struct rk1000_tve *rk1000 = bridge_to_rk1000(bridge); + + dev_dbg(rk1000->dev, "%s\n", __func__); + ret = rk1000_tve_enable(rk1000); + if (ret) + dev_err(rk1000->dev, "rk1000 enable failed\n"); +} + +static void rk1000_bridge_disable(struct drm_bridge *bridge) +{ + struct rk1000_tve *rk1000 = bridge_to_rk1000(bridge); + + dev_dbg(rk1000->dev, "%s\n", __func__); + rk1000_tve_disable(rk1000); +} + +static int rk1000_bridge_attach(struct drm_bridge *bridge) +{ + struct rk1000_tve *rk1000 = bridge_to_rk1000(bridge); + int ret; + + if (!bridge->encoder) { + dev_err(rk1000->dev, "Parent encoder object not found\n"); + return -ENODEV; + } + + rk1000->encoder = bridge->encoder; + ret = drm_connector_init(bridge->dev, &rk1000->connector, + &rk1000_connector_funcs, + DRM_MODE_CONNECTOR_TV); + if (ret) { + dev_err(rk1000->dev, "Failed to initialize connector\n"); + return ret; + } + drm_connector_helper_add(&rk1000->connector, + &rk1000_connector_helper_funcs); + ret = drm_mode_connector_attach_encoder(&rk1000->connector, + bridge->encoder); + if (ret) + dev_err(rk1000->dev, "rk1000 attach failed ret:%d", ret); + + return ret; +} + +static struct drm_bridge_funcs rk1000_bridge_funcs = { + .enable = rk1000_bridge_enable, + .disable = rk1000_bridge_disable, + .mode_set = rk1000_bridge_mode_set, + .attach = rk1000_bridge_attach, +}; + +static int rk1000_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct device_node *np; + struct device_node *ctl; + struct rk1000_tve *rk1000; + struct i2c_client *ctl_client; + + rk1000 = devm_kzalloc(&client->dev, sizeof(*rk1000), GFP_KERNEL); + if (!rk1000) + return -ENOMEM; + + np = client->dev.of_node; + rk1000->client = client; + rk1000->dev = &client->dev; + rk1000->mode = CVBS_PAL; + + rk1000->tvemap = devm_regmap_init_i2c(client, + &rk1000_tve_regmap_config); + if (IS_ERR(rk1000->tvemap)) { + ret = PTR_ERR(rk1000->tvemap); + dev_err(rk1000->dev, "Failed to initialize tve regmap: %d\n", + ret); + return ret; + } + + ctl = of_parse_phandle(np, "rockchip,ctl", 0); + if (!ctl) { + dev_err(rk1000->dev, "rk1000 can't find control node\n"); + return -EINVAL; + } + + ctl_client = of_find_i2c_device_by_node(ctl); + if (!ctl_client) { + dev_err(rk1000->dev, "rk1000 can't find control client\n"); + return -EPROBE_DEFER; + } + + rk1000->ctlmap = devm_regmap_init_i2c(ctl_client, + &rk1000_ctl_regmap_config); + if (IS_ERR(rk1000->ctlmap)) { + ret = PTR_ERR(rk1000->ctlmap); + dev_err(rk1000->dev, "Failed to initialize ctl regmap: %d\n", + ret); + return ret; + } + + rk1000->bridge.funcs = &rk1000_bridge_funcs; + rk1000->bridge.of_node = rk1000->dev->of_node; + + ret = drm_bridge_add(&rk1000->bridge); + if (ret) { + dev_err(rk1000->dev, "failed to add rk1000 bridge\n"); + return ret; + } + + i2c_set_clientdata(client, rk1000); + dev_dbg(rk1000->dev, "rk1000 probe ok\n"); + + return 0; +} + +static int rk1000_remove(struct i2c_client *client) +{ + struct rk1000_tve *rk1000 = i2c_get_clientdata(client); + + drm_bridge_remove(&rk1000->bridge); + + return 0; +} + +static const struct i2c_device_id rk1000_i2c_id[] = { + { "rk1000-tve", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, rk1000_i2c_id); + +static const struct of_device_id rk1000_dt_ids[] = { + { .compatible = "rockchip,rk1000-tve" }, + { } +}; + +MODULE_DEVICE_TABLE(of, rk1000_dt_ids); + +static struct i2c_driver rk1000_driver = { + .driver = { + .name = "rk1000-tve", + .of_match_table = of_match_ptr(rk1000_dt_ids), + }, + .id_table = rk1000_i2c_id, + .probe = rk1000_probe, + .remove = rk1000_remove, +}; +module_i2c_driver(rk1000_driver); + +MODULE_AUTHOR("Algea Cao "); +MODULE_DESCRIPTION("ROCKCHIP RK1000 Driver"); +MODULE_LICENSE("GPL v2");