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");