diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511.h | 71 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 100 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7533.c | 100 |
5 files changed, 230 insertions, 49 deletions
diff --git a/drivers/gpu/drm/bridge/adv7511/Kconfig b/drivers/gpu/drm/bridge/adv7511/Kconfig index 222c6cc9600c..eb844199d148 100644 --- a/drivers/gpu/drm/bridge/adv7511/Kconfig +++ b/drivers/gpu/drm/bridge/adv7511/Kconfig @@ -5,3 +5,10 @@ config DRM_I2C_ADV7511 select REGMAP_I2C help Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders. + +config DRM_I2C_ADV7533 + bool "ADV7533 encoder" + depends on DRM_I2C_ADV7511 + default y + help + Support for the Analog Devices ADV7533 DSI to HDMI encoder. diff --git a/drivers/gpu/drm/bridge/adv7511/Makefile b/drivers/gpu/drm/bridge/adv7511/Makefile index 692f83a07b64..9019327fff4c 100644 --- a/drivers/gpu/drm/bridge/adv7511/Makefile +++ b/drivers/gpu/drm/bridge/adv7511/Makefile @@ -1,2 +1,3 @@ adv7511-y := adv7511_drv.o +adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 38515b30cedf..5dea769c3c71 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -10,6 +10,10 @@ #define __DRM_I2C_ADV7511_H__ #include <linux/hdmi.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include <drm/drm_crtc_helper.h> #define ADV7511_REG_CHIP_REVISION 0x00 #define ADV7511_REG_N0 0x01 @@ -286,4 +290,71 @@ struct adv7511_video_config { struct hdmi_avi_infoframe avi_infoframe; }; +enum adv7511_type { + ADV7511, + ADV7533, +}; + +struct adv7511 { + struct i2c_client *i2c_main; + struct i2c_client *i2c_edid; + struct i2c_client *i2c_cec; + + struct regmap *regmap; + struct regmap *regmap_cec; + enum drm_connector_status status; + bool powered; + + unsigned int f_tmds; + + unsigned int current_edid_segment; + uint8_t edid_buf[256]; + bool edid_read; + + wait_queue_head_t wq; + struct drm_bridge bridge; + struct drm_connector connector; + + bool embedded_sync; + enum adv7511_sync_polarity vsync_polarity; + enum adv7511_sync_polarity hsync_polarity; + bool rgb; + + struct edid *edid; + + struct gpio_desc *gpio_pd; + + enum adv7511_type type; +}; + +#ifdef CONFIG_DRM_I2C_ADV7533 +void adv7533_dsi_power_on(struct adv7511 *adv); +void adv7533_dsi_power_off(struct adv7511 *adv); +int adv7533_patch_registers(struct adv7511 *adv); +void adv7533_uninit_cec(struct adv7511 *adv); +int adv7533_init_cec(struct adv7511 *adv); +#else +static inline void adv7533_dsi_power_on(struct adv7511 *adv) +{ +} + +static inline void adv7533_dsi_power_off(struct adv7511 *adv) +{ +} + +static inline int adv7533_patch_registers(struct adv7511 *adv) +{ + return -ENODEV; +} + +static inline void adv7533_uninit_cec(struct adv7511 *adv) +{ +} + +static inline int adv7533_init_cec(struct adv7511 *adv) +{ + return -ENODEV; +} +#endif + #endif /* __DRM_I2C_ADV7511_H__ */ diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 1fff0ab29b7a..e33702bbd5d5 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -8,48 +8,17 @@ #include <linux/device.h> #include <linux/gpio/consumer.h> -#include <linux/i2c.h> #include <linux/module.h> -#include <linux/regmap.h> +#include <linux/of_device.h> #include <linux/slab.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include "adv7511.h" -struct adv7511 { - struct i2c_client *i2c_main; - struct i2c_client *i2c_edid; - - struct regmap *regmap; - struct regmap *packet_memory_regmap; - enum drm_connector_status status; - bool powered; - - unsigned int f_tmds; - - unsigned int current_edid_segment; - uint8_t edid_buf[256]; - bool edid_read; - - wait_queue_head_t wq; - struct drm_bridge bridge; - struct drm_connector connector; - - bool embedded_sync; - enum adv7511_sync_polarity vsync_polarity; - enum adv7511_sync_polarity hsync_polarity; - bool rgb; - - struct edid *edid; - - struct gpio_desc *gpio_pd; -}; - /* ADI recommended values for proper operation. */ static const struct reg_sequence adv7511_fixed_registers[] = { { 0x98, 0x03 }, @@ -391,6 +360,9 @@ static void adv7511_power_on(struct adv7511 *adv7511) */ regcache_sync(adv7511->regmap); + if (adv7511->type == ADV7533) + adv7533_dsi_power_on(adv7511); + adv7511->powered = true; } @@ -402,6 +374,9 @@ static void adv7511_power_off(struct adv7511 *adv7511) ADV7511_POWER_POWER_DOWN); regcache_mark_dirty(adv7511->regmap); + if (adv7511->type == ADV7533) + adv7533_dsi_power_off(adv7511); + adv7511->powered = false; } @@ -862,8 +837,6 @@ static int adv7511_parse_dt(struct device_node *np, const char *str; int ret; - memset(config, 0, sizeof(*config)); - of_property_read_u32(np, "adi,input-depth", &config->input_color_depth); if (config->input_color_depth != 8 && config->input_color_depth != 10 && config->input_color_depth != 12) @@ -963,9 +936,18 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) adv7511->powered = false; adv7511->status = connector_status_disconnected; - ret = adv7511_parse_dt(dev->of_node, &link_config); - if (ret) - return ret; + if (dev->of_node) + adv7511->type = (enum adv7511_type)of_device_get_match_data(dev); + else + adv7511->type = id->driver_data; + + memset(&link_config, 0, sizeof(link_config)); + + if (adv7511->type == ADV7511) { + ret = adv7511_parse_dt(dev->of_node, &link_config); + if (ret) + return ret; + } /* * The power down GPIO is optional. If present, toggle it from active to @@ -989,8 +971,12 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) return ret; dev_dbg(dev, "Rev. %d\n", val); - ret = regmap_register_patch(adv7511->regmap, adv7511_fixed_registers, - ARRAY_SIZE(adv7511_fixed_registers)); + if (adv7511->type == ADV7511) + ret = regmap_register_patch(adv7511->regmap, + adv7511_fixed_registers, + ARRAY_SIZE(adv7511_fixed_registers)); + else + ret = adv7533_patch_registers(adv7511); if (ret) return ret; @@ -1005,6 +991,12 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (!adv7511->i2c_edid) return -ENOMEM; + if (adv7511->type == ADV7533) { + ret = adv7533_init_cec(adv7511); + if (ret) + goto err_i2c_unregister_edid; + } + if (i2c->irq) { init_waitqueue_head(&adv7511->wq); @@ -1013,7 +1005,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) IRQF_ONESHOT, dev_name(dev), adv7511); if (ret) - goto err_i2c_unregister_device; + goto err_unregister_cec; } /* CEC is unused for now */ @@ -1024,7 +1016,8 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) i2c_set_clientdata(i2c, adv7511); - adv7511_set_link_config(adv7511, &link_config); + if (adv7511->type == ADV7511) + adv7511_set_link_config(adv7511, &link_config); adv7511->bridge.funcs = &adv7511_bridge_funcs; adv7511->bridge.of_node = dev->of_node; @@ -1032,12 +1025,14 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) ret = drm_bridge_add(&adv7511->bridge); if (ret) { dev_err(dev, "failed to add adv7511 bridge\n"); - goto err_i2c_unregister_device; + goto err_unregister_cec; } return 0; -err_i2c_unregister_device: +err_unregister_cec: + adv7533_uninit_cec(adv7511); +err_i2c_unregister_edid: i2c_unregister_device(adv7511->i2c_edid); return ret; @@ -1049,6 +1044,7 @@ static int adv7511_remove(struct i2c_client *i2c) drm_bridge_remove(&adv7511->bridge); + adv7533_uninit_cec(adv7511); i2c_unregister_device(adv7511->i2c_edid); kfree(adv7511->edid); @@ -1057,17 +1053,23 @@ static int adv7511_remove(struct i2c_client *i2c) } static const struct i2c_device_id adv7511_i2c_ids[] = { - { "adv7511", 0 }, - { "adv7511w", 0 }, - { "adv7513", 0 }, + { "adv7511", ADV7511 }, + { "adv7511w", ADV7511 }, + { "adv7513", ADV7511 }, +#ifdef CONFIG_DRM_I2C_ADV7533 + { "adv7533", ADV7533 }, +#endif { } }; MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids); static const struct of_device_id adv7511_of_ids[] = { - { .compatible = "adi,adv7511", }, - { .compatible = "adi,adv7511w", }, - { .compatible = "adi,adv7513", }, + { .compatible = "adi,adv7511", .data = (void *)ADV7511 }, + { .compatible = "adi,adv7511w", .data = (void *)ADV7511 }, + { .compatible = "adi,adv7513", .data = (void *)ADV7511 }, +#ifdef CONFIG_DRM_I2C_ADV7533 + { .compatible = "adi,adv7533", .data = (void *)ADV7533 }, +#endif { } }; MODULE_DEVICE_TABLE(of, adv7511_of_ids); diff --git a/drivers/gpu/drm/bridge/adv7511/adv7533.c b/drivers/gpu/drm/bridge/adv7511/adv7533.c new file mode 100644 index 000000000000..cb4ca640a2b3 --- /dev/null +++ b/drivers/gpu/drm/bridge/adv7511/adv7533.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "adv7511.h" + +static const struct reg_sequence adv7533_fixed_registers[] = { + { 0x16, 0x20 }, + { 0x9a, 0xe0 }, + { 0xba, 0x70 }, + { 0xde, 0x82 }, + { 0xe4, 0x40 }, + { 0xe5, 0x80 }, +}; + +static const struct reg_sequence adv7533_cec_fixed_registers[] = { + { 0x15, 0xd0 }, + { 0x17, 0xd0 }, + { 0x24, 0x20 }, + { 0x57, 0x11 }, +}; + +static const struct regmap_config adv7533_cec_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_RBTREE, +}; + +void adv7533_dsi_power_on(struct adv7511 *adv) +{ + /* set number of dsi lanes (hardcoded to 4 for now) */ + regmap_write(adv->regmap_cec, 0x1c, 4 << 4); + /* disable internal timing generator */ + regmap_write(adv->regmap_cec, 0x27, 0x0b); + /* enable hdmi */ + regmap_write(adv->regmap_cec, 0x03, 0x89); + /* disable test mode */ + regmap_write(adv->regmap_cec, 0x55, 0x00); + + regmap_register_patch(adv->regmap_cec, adv7533_cec_fixed_registers, + ARRAY_SIZE(adv7533_cec_fixed_registers)); +} + +void adv7533_dsi_power_off(struct adv7511 *adv) +{ + /* disable hdmi */ + regmap_write(adv->regmap_cec, 0x03, 0x0b); +} + +int adv7533_patch_registers(struct adv7511 *adv) +{ + return regmap_register_patch(adv->regmap, + adv7533_fixed_registers, + ARRAY_SIZE(adv7533_fixed_registers)); +} + +void adv7533_uninit_cec(struct adv7511 *adv) +{ + i2c_unregister_device(adv->i2c_cec); +} + +static const int cec_i2c_addr = 0x78; + +int adv7533_init_cec(struct adv7511 *adv) +{ + int ret; + + adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter, cec_i2c_addr >> 1); + if (!adv->i2c_cec) + return -ENOMEM; + + adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec, + &adv7533_cec_regmap_config); + if (IS_ERR(adv->regmap_cec)) { + ret = PTR_ERR(adv->regmap_cec); + goto err; + } + + ret = regmap_register_patch(adv->regmap_cec, + adv7533_cec_fixed_registers, + ARRAY_SIZE(adv7533_cec_fixed_registers)); + if (ret) + goto err; + + return 0; +err: + adv7533_uninit_cec(adv); + return ret; +} |