mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-31 22:23:05 +00:00
media: i2c: ds90ub960: Configure serializer using back-channel
For DS90UB9702-Q1, it is recommended to configure some serializer settings over the back-channel before the forward-channel is active. This can only be done if the serializer's I2C address on the FPD-Link bus is populated in the device tree node. Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
This commit is contained in:
parent
b8e2193b1e
commit
e2a3b695bc
@ -92,4 +92,8 @@
|
||||
#define UB953_IND_PGEN_VFP 0x0f
|
||||
#define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */
|
||||
|
||||
/* UB971 Registers */
|
||||
|
||||
#define UB971_ENH_BC_CHK 0x4b
|
||||
|
||||
#endif /* __MEDIA_I2C_DS90UB953_H__ */
|
||||
|
@ -52,6 +52,8 @@
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
#include "ds90ub953.h"
|
||||
|
||||
#define MHZ(v) ((u32)((v) * HZ_PER_MHZ))
|
||||
|
||||
/*
|
||||
@ -244,13 +246,16 @@
|
||||
|
||||
#define UB960_RR_BCC_CONFIG 0x58
|
||||
#define UB960_RR_BCC_CONFIG_BC_ALWAYS_ON BIT(4)
|
||||
#define UB960_RR_BCC_CONFIG_AUTO_ACK_ALL BIT(5)
|
||||
#define UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH BIT(6)
|
||||
#define UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK GENMASK(2, 0)
|
||||
|
||||
#define UB960_RR_DATAPATH_CTL1 0x59
|
||||
#define UB960_RR_DATAPATH_CTL2 0x5a
|
||||
#define UB960_RR_SER_ID 0x5b
|
||||
#define UB960_RR_SER_ID_FREEZE_DEVICE_ID BIT(0)
|
||||
#define UB960_RR_SER_ALIAS_ID 0x5c
|
||||
#define UB960_RR_SER_ALIAS_ID_AUTO_ACK BIT(0)
|
||||
|
||||
/* For these two register sets: n < UB960_MAX_PORT_ALIASES */
|
||||
#define UB960_RR_SLAVE_ID(n) (0x5d + (n))
|
||||
@ -486,7 +491,9 @@ struct ub960_rxport {
|
||||
struct fwnode_handle *fwnode;
|
||||
struct i2c_client *client;
|
||||
unsigned short alias; /* I2C alias (lower 7 bits) */
|
||||
short addr; /* Local I2C address (lower 7 bits) */
|
||||
struct ds90ub9xx_platform_data pdata;
|
||||
struct regmap *regmap;
|
||||
} ser;
|
||||
|
||||
enum ub960_rxport_mode rx_mode;
|
||||
@ -1984,6 +1991,78 @@ static unsigned long ub960_calc_bc_clk_rate_ub9702(struct ub960_data *priv,
|
||||
}
|
||||
}
|
||||
|
||||
static int ub960_rxport_serializer_write(struct ub960_rxport *rxport, u8 reg,
|
||||
u8 val, int *err)
|
||||
{
|
||||
struct ub960_data *priv = rxport->priv;
|
||||
struct device *dev = &priv->client->dev;
|
||||
union i2c_smbus_data data;
|
||||
int ret;
|
||||
|
||||
if (err && *err)
|
||||
return *err;
|
||||
|
||||
data.byte = val;
|
||||
|
||||
ret = i2c_smbus_xfer(priv->client->adapter, rxport->ser.alias, 0,
|
||||
I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, &data);
|
||||
if (ret)
|
||||
dev_err(dev,
|
||||
"rx%u: cannot write serializer register 0x%02x (%d)!\n",
|
||||
rxport->nport, reg, ret);
|
||||
|
||||
if (ret && err)
|
||||
*err = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ub960_rxport_bc_ser_config(struct ub960_rxport *rxport)
|
||||
{
|
||||
struct ub960_data *priv = rxport->priv;
|
||||
struct device *dev = &priv->client->dev;
|
||||
u8 nport = rxport->nport;
|
||||
int ret;
|
||||
|
||||
/* Skip port if serializer's address is not known */
|
||||
if (rxport->ser.addr < 0) {
|
||||
dev_dbg(dev,
|
||||
"rx%u: serializer address missing, skip configuration\n",
|
||||
nport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: the code here probably only works for CSI-2 serializers in
|
||||
* sync mode. To support other serializers the BC related configuration
|
||||
* should be done before calling this function.
|
||||
*/
|
||||
|
||||
/* Enable I2C passthrough and auto-ack on BC */
|
||||
ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG,
|
||||
UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH |
|
||||
UB960_RR_BCC_CONFIG_AUTO_ACK_ALL,
|
||||
UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH |
|
||||
UB960_RR_BCC_CONFIG_AUTO_ACK_ALL,
|
||||
&ret);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable BC alternate mode auto detect */
|
||||
ub960_rxport_serializer_write(rxport, UB971_ENH_BC_CHK, 0x02, &ret);
|
||||
/* Decrease link detect timer */
|
||||
ub960_rxport_serializer_write(rxport, UB953_REG_BC_CTRL, 0x06, &ret);
|
||||
|
||||
/* Disable I2C passthrough and auto-ack on BC */
|
||||
ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG,
|
||||
UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH |
|
||||
UB960_RR_BCC_CONFIG_AUTO_ACK_ALL,
|
||||
0x0, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport)
|
||||
{
|
||||
struct ub960_rxport *rxport = priv->rxports[nport];
|
||||
@ -2860,6 +2939,36 @@ static int ub960_init_rx_ports_ub9702(struct ub960_data *priv)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for_each_active_rxport(priv, it) {
|
||||
if (it.rxport->ser.addr >= 0) {
|
||||
/*
|
||||
* Set serializer's I2C address if set in the dts file,
|
||||
* and freeze it to prevent updates from the FC.
|
||||
*/
|
||||
ub960_rxport_write(priv, it.nport, UB960_RR_SER_ID,
|
||||
it.rxport->ser.addr << 1 |
|
||||
UB960_RR_SER_ID_FREEZE_DEVICE_ID,
|
||||
&ret);
|
||||
}
|
||||
|
||||
/* Set serializer I2C alias with auto-ack */
|
||||
ub960_rxport_write(priv, it.nport, UB960_RR_SER_ALIAS_ID,
|
||||
it.rxport->ser.alias << 1 |
|
||||
UB960_RR_SER_ALIAS_ID_AUTO_ACK, &ret);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_active_rxport(priv, it) {
|
||||
if (fwnode_device_is_compatible(it.rxport->ser.fwnode,
|
||||
"ti,ds90ub971-q1")) {
|
||||
ret = ub960_rxport_bc_ser_config(it.rxport);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_active_rxport_fpd4(priv, it) {
|
||||
/* Hold state machine in reset */
|
||||
ub960_rxport_write(priv, it.nport, UB9702_RR_RX_SM_SEL_2, 0x10,
|
||||
@ -2988,16 +3097,17 @@ static int ub960_init_rx_ports_ub9702(struct ub960_data *priv)
|
||||
ub960_rxport_write(priv, it.nport, UB960_RR_PORT_ICR_LO, 0x7f,
|
||||
&ret);
|
||||
|
||||
/* Clear serializer I2C alias auto-ack */
|
||||
ub960_rxport_update_bits(priv, it.nport, UB960_RR_SER_ALIAS_ID,
|
||||
UB960_RR_SER_ALIAS_ID_AUTO_ACK, 0,
|
||||
&ret);
|
||||
|
||||
/* Enable I2C_PASS_THROUGH */
|
||||
ub960_rxport_update_bits(priv, it.nport, UB960_RR_BCC_CONFIG,
|
||||
UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH,
|
||||
UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH,
|
||||
&ret);
|
||||
|
||||
/* Enable I2C communication to the serializer via the alias */
|
||||
ub960_rxport_write(priv, it.nport, UB960_RR_SER_ALIAS_ID,
|
||||
it.rxport->ser.alias << 1, &ret);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -4156,6 +4266,7 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv,
|
||||
s32 strobe_pos;
|
||||
u32 eq_level;
|
||||
u32 ser_i2c_alias;
|
||||
u32 ser_i2c_addr;
|
||||
int ret;
|
||||
|
||||
cdr_mode = RXPORT_CDR_FPD3;
|
||||
@ -4267,6 +4378,13 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = fwnode_property_read_u32(rxport->ser.fwnode, "reg",
|
||||
&ser_i2c_addr);
|
||||
if (ret)
|
||||
rxport->ser.addr = -EINVAL;
|
||||
else
|
||||
rxport->ser.addr = ser_i2c_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user