Commit 2437e7cd authored by Archit Taneja's avatar Archit Taneja

drm/bridge: adv7533: Initial support for ADV7533

ADV7533 is a DSI to HDMI encoder chip. It is a derivative of ADV7511,
with additional blocks to translate input DSI data to parallel RGB
data. Besides the ADV7511 I2C register map, it has additional registers
that require to be configured to activate the DSI Rx block.

Create a new config that enables ADV7533 support. Use DT compatible
strings to populate the ADV7533 type enum. Add minimal register
configurations belonging to the DSI/CEC register map. Keep the ADV7533
code in a separate file.

Originally worked on by Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: default avatarArchit Taneja <architt@codeaurora.org>
parent f0bfcc22
......@@ -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.
adv7511-y := adv7511_drv.o
adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o
......@@ -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__ */
......@@ -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;
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,
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,6 +1016,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
i2c_set_clientdata(i2c, adv7511);
if (adv7511->type == ADV7511)
adv7511_set_link_config(adv7511, &link_config);
adv7511->bridge.funcs = &adv7511_bridge_funcs;
......@@ -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);
......
/*
* 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment