mirror of https://gitee.com/openkylin/linux.git
drm/bridge: ti-sn65dsi86: Implement AUX channel
This was hand-rolled in the first version, and will surely be useful as we expand the driver to support more varied use cases. Changes in v2: - Change subject prefix s/panel/bridge/ - Downgrade warning in poll function to error message - Fix DP_EDP_CONFIGURATION_SET write value (Sandeep) - Mask upper 8 bits of msg->address (Sandeep) - Check aux cmd status for errors after completing the send (Sandeep) - Remove length check since it's covered in the aux status - Flip the READ check in transfer to WRITE check + early exit Changes in v3: - Added to the set - Wrapped (x) in WDATA/RDATA #defines - Replace readx_poll* with regmap_read_poll* Cc: Sandeep Panda <spanda@codeaurora.org> Reviewed-by: Sandeep Panda <spanda@codeaurora.org> Signed-off-by: Sean Paul <seanpaul@chromium.org> Link: https://patchwork.freedesktop.org/patch/msgid/20180813213058.184821-4-sean@poorly.run
This commit is contained in:
parent
6c76c0eb03
commit
b814ec6d45
|
@ -7,12 +7,14 @@
|
|||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -46,7 +48,7 @@
|
|||
#define SN_DATA_FORMAT_REG 0x5B
|
||||
#define SN_HPD_DISABLE_REG 0x5C
|
||||
#define HPD_DISABLE BIT(0)
|
||||
#define SN_AUX_WDATA0_REG 0x64
|
||||
#define SN_AUX_WDATA_REG(x) (0x64 + (x))
|
||||
#define SN_AUX_ADDR_19_16_REG 0x74
|
||||
#define SN_AUX_ADDR_15_8_REG 0x75
|
||||
#define SN_AUX_ADDR_7_0_REG 0x76
|
||||
|
@ -54,6 +56,7 @@
|
|||
#define SN_AUX_CMD_REG 0x78
|
||||
#define AUX_CMD_SEND BIT(1)
|
||||
#define AUX_CMD_REQ(x) ((x) << 4)
|
||||
#define SN_AUX_RDATA_REG(x) (0x79 + (x))
|
||||
#define SN_SSC_CONFIG_REG 0x93
|
||||
#define DP_NUM_LANES_MASK GENMASK(5, 4)
|
||||
#define DP_NUM_LANES(x) ((x) << 4)
|
||||
|
@ -63,6 +66,10 @@
|
|||
#define SN_ML_TX_MODE_REG 0x96
|
||||
#define ML_TX_MAIN_LINK_OFF 0
|
||||
#define ML_TX_NORMAL_MODE BIT(0)
|
||||
#define SN_AUX_CMD_STATUS_REG 0xF4
|
||||
#define AUX_IRQ_STATUS_AUX_RPLY_TOUT BIT(3)
|
||||
#define AUX_IRQ_STATUS_AUX_SHORT BIT(5)
|
||||
#define AUX_IRQ_STATUS_NAT_I2C_FAIL BIT(6)
|
||||
|
||||
#define MIN_DSI_CLK_FREQ_MHZ 40
|
||||
|
||||
|
@ -70,11 +77,15 @@
|
|||
#define DP_CLK_FUDGE_NUM 10
|
||||
#define DP_CLK_FUDGE_DEN 8
|
||||
|
||||
/* Matches DP_AUX_MAX_PAYLOAD_BYTES (for now) */
|
||||
#define SN_AUX_MAX_PAYLOAD_BYTES 16
|
||||
|
||||
#define SN_REGULATOR_SUPPLY_NUM 4
|
||||
|
||||
struct ti_sn_bridge {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct drm_dp_aux aux;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_connector connector;
|
||||
struct device_node *host_node;
|
||||
|
@ -471,13 +482,8 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge)
|
|||
* authentication method. We need to enable this method in the eDP panel
|
||||
* at DisplayPort address 0x0010A prior to link training.
|
||||
*/
|
||||
regmap_write(pdata->regmap, SN_AUX_WDATA0_REG, 0x01);
|
||||
regmap_write(pdata->regmap, SN_AUX_ADDR_19_16_REG, 0x00);
|
||||
regmap_write(pdata->regmap, SN_AUX_ADDR_15_8_REG, 0x01);
|
||||
regmap_write(pdata->regmap, SN_AUX_ADDR_7_0_REG, 0x0A);
|
||||
regmap_write(pdata->regmap, SN_AUX_LENGTH_REG, 0x01);
|
||||
regmap_write(pdata->regmap, SN_AUX_CMD_REG, 0x81);
|
||||
usleep_range(10000, 10500); /* 10ms delay recommended by spec */
|
||||
drm_dp_dpcd_writeb(&pdata->aux, DP_EDP_CONFIGURATION_SET,
|
||||
DP_ALTERNATE_SCRAMBLER_RESET_ENABLE);
|
||||
|
||||
/* Semi auto link training mode */
|
||||
regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0x0A);
|
||||
|
@ -525,6 +531,82 @@ static const struct drm_bridge_funcs ti_sn_bridge_funcs = {
|
|||
.post_disable = ti_sn_bridge_post_disable,
|
||||
};
|
||||
|
||||
static struct ti_sn_bridge *aux_to_ti_sn_bridge(struct drm_dp_aux *aux)
|
||||
{
|
||||
return container_of(aux, struct ti_sn_bridge, aux);
|
||||
}
|
||||
|
||||
static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
struct ti_sn_bridge *pdata = aux_to_ti_sn_bridge(aux);
|
||||
u32 request = msg->request & ~DP_AUX_I2C_MOT;
|
||||
u32 request_val = AUX_CMD_REQ(msg->request);
|
||||
u8 *buf = (u8 *)msg->buffer;
|
||||
unsigned int val;
|
||||
int ret, i;
|
||||
|
||||
if (msg->size > SN_AUX_MAX_PAYLOAD_BYTES)
|
||||
return -EINVAL;
|
||||
|
||||
switch (request) {
|
||||
case DP_AUX_NATIVE_WRITE:
|
||||
case DP_AUX_I2C_WRITE:
|
||||
case DP_AUX_NATIVE_READ:
|
||||
case DP_AUX_I2C_READ:
|
||||
regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_write(pdata->regmap, SN_AUX_ADDR_19_16_REG,
|
||||
(msg->address >> 16) & 0xF);
|
||||
regmap_write(pdata->regmap, SN_AUX_ADDR_15_8_REG,
|
||||
(msg->address >> 8) & 0xFF);
|
||||
regmap_write(pdata->regmap, SN_AUX_ADDR_7_0_REG, msg->address & 0xFF);
|
||||
|
||||
regmap_write(pdata->regmap, SN_AUX_LENGTH_REG, msg->size);
|
||||
|
||||
if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE) {
|
||||
for (i = 0; i < msg->size; i++)
|
||||
regmap_write(pdata->regmap, SN_AUX_WDATA_REG(i),
|
||||
buf[i]);
|
||||
}
|
||||
|
||||
regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val | AUX_CMD_SEND);
|
||||
|
||||
ret = regmap_read_poll_timeout(pdata->regmap, SN_AUX_CMD_REG, val,
|
||||
!(val & AUX_CMD_SEND), 200,
|
||||
50 * 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(pdata->regmap, SN_AUX_CMD_STATUS_REG, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
else if ((val & AUX_IRQ_STATUS_NAT_I2C_FAIL)
|
||||
|| (val & AUX_IRQ_STATUS_AUX_RPLY_TOUT)
|
||||
|| (val & AUX_IRQ_STATUS_AUX_SHORT))
|
||||
return -ENXIO;
|
||||
|
||||
if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE)
|
||||
return msg->size;
|
||||
|
||||
for (i = 0; i < msg->size; i++) {
|
||||
unsigned int val;
|
||||
ret = regmap_read(pdata->regmap, SN_AUX_RDATA_REG(i),
|
||||
&val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
WARN_ON(val & ~0xFF);
|
||||
buf[i] = (u8)(val & 0xFF);
|
||||
}
|
||||
|
||||
return msg->size;
|
||||
}
|
||||
|
||||
static int ti_sn_bridge_parse_dsi_host(struct ti_sn_bridge *pdata)
|
||||
{
|
||||
struct device_node *np = pdata->dev->of_node;
|
||||
|
@ -604,6 +686,11 @@ static int ti_sn_bridge_probe(struct i2c_client *client,
|
|||
|
||||
i2c_set_clientdata(client, pdata);
|
||||
|
||||
pdata->aux.name = "ti-sn65dsi86-aux";
|
||||
pdata->aux.dev = pdata->dev;
|
||||
pdata->aux.transfer = ti_sn_aux_transfer;
|
||||
drm_dp_aux_register(&pdata->aux);
|
||||
|
||||
pdata->bridge.funcs = &ti_sn_bridge_funcs;
|
||||
pdata->bridge.of_node = client->dev.of_node;
|
||||
|
||||
|
|
Loading…
Reference in New Issue