mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-06 00:39:48 +00:00
drm/bridge: ti-sn65dsi86: Group code in sections
Reorganize the functions in sections, related to connector operations, bridge operations, AUX adapter, GPIO controller and probe & remove. This prepares for proper support of DRM_BRIDGE_ATTACH_NO_CONNECTOR that will add more functions, to ensure that the code will stay readable. No functional change intended. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Reviewed-by: Stephen Boyd <swboyd@chromium.org> Reviewed-by: Douglas Anderson <dianders@chromium.org> Signed-off-by: Robert Foss <robert.foss@linaro.org> Link: https://patchwork.freedesktop.org/patch/msgid/20210624000304.16281-6-laurent.pinchart+renesas@ideasonboard.com
This commit is contained in:
parent
4e5763f03e
commit
77674e722f
1 changed files with 330 additions and 307 deletions
|
@ -394,7 +394,211 @@ static void ti_sn65dsi86_debugfs_init(struct ti_sn65dsi86 *pdata)
|
||||||
debugfs_create_file("status", 0600, debugfs, pdata, &status_fops);
|
debugfs_create_file("status", 0600, debugfs, pdata, &status_fops);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Connector funcs */
|
/* -----------------------------------------------------------------------------
|
||||||
|
* Auxiliary Devices (*not* AUX)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void ti_sn65dsi86_uninit_aux(void *data)
|
||||||
|
{
|
||||||
|
auxiliary_device_uninit(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ti_sn65dsi86_delete_aux(void *data)
|
||||||
|
{
|
||||||
|
auxiliary_device_delete(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AUX bus docs say that a non-NULL release is mandatory, but it makes no
|
||||||
|
* sense for the model used here where all of the aux devices are allocated
|
||||||
|
* in the single shared structure. We'll use this noop as a workaround.
|
||||||
|
*/
|
||||||
|
static void ti_sn65dsi86_noop(struct device *dev) {}
|
||||||
|
|
||||||
|
static int ti_sn65dsi86_add_aux_device(struct ti_sn65dsi86 *pdata,
|
||||||
|
struct auxiliary_device *aux,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
struct device *dev = pdata->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
aux->name = name;
|
||||||
|
aux->dev.parent = dev;
|
||||||
|
aux->dev.release = ti_sn65dsi86_noop;
|
||||||
|
device_set_of_node_from_dev(&aux->dev, dev);
|
||||||
|
ret = auxiliary_device_init(aux);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
ret = devm_add_action_or_reset(dev, ti_sn65dsi86_uninit_aux, aux);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = auxiliary_device_add(aux);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
ret = devm_add_action_or_reset(dev, ti_sn65dsi86_delete_aux, aux);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* AUX Adapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct ti_sn65dsi86 *aux_to_ti_sn65dsi86(struct drm_dp_aux *aux)
|
||||||
|
{
|
||||||
|
return container_of(aux, struct ti_sn65dsi86, aux);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,
|
||||||
|
struct drm_dp_aux_msg *msg)
|
||||||
|
{
|
||||||
|
struct ti_sn65dsi86 *pdata = aux_to_ti_sn65dsi86(aux);
|
||||||
|
u32 request = msg->request & ~(DP_AUX_I2C_MOT | DP_AUX_I2C_WRITE_STATUS_UPDATE);
|
||||||
|
u32 request_val = AUX_CMD_REQ(msg->request);
|
||||||
|
u8 *buf = msg->buffer;
|
||||||
|
unsigned int len = msg->size;
|
||||||
|
unsigned int val;
|
||||||
|
int ret;
|
||||||
|
u8 addr_len[SN_AUX_LENGTH_REG + 1 - SN_AUX_ADDR_19_16_REG];
|
||||||
|
|
||||||
|
if (len > SN_AUX_MAX_PAYLOAD_BYTES)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
pm_runtime_get_sync(pdata->dev);
|
||||||
|
mutex_lock(&pdata->comms_mutex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If someone tries to do a DDC over AUX transaction before pre_enable()
|
||||||
|
* on a device without a dedicated reference clock then we just can't
|
||||||
|
* do it. Fail right away. This prevents non-refclk users from reading
|
||||||
|
* the EDID before enabling the panel but such is life.
|
||||||
|
*/
|
||||||
|
if (!pdata->comms_enabled) {
|
||||||
|
ret = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
/* Assume it's good */
|
||||||
|
msg->reply = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
BUILD_BUG_ON(sizeof(addr_len) != sizeof(__be32));
|
||||||
|
put_unaligned_be32((msg->address & SN_AUX_ADDR_MASK) << 8 | len,
|
||||||
|
addr_len);
|
||||||
|
regmap_bulk_write(pdata->regmap, SN_AUX_ADDR_19_16_REG, addr_len,
|
||||||
|
ARRAY_SIZE(addr_len));
|
||||||
|
|
||||||
|
if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE)
|
||||||
|
regmap_bulk_write(pdata->regmap, SN_AUX_WDATA_REG(0), buf, len);
|
||||||
|
|
||||||
|
/* Clear old status bits before start so we don't get confused */
|
||||||
|
regmap_write(pdata->regmap, SN_AUX_CMD_STATUS_REG,
|
||||||
|
AUX_IRQ_STATUS_NAT_I2C_FAIL |
|
||||||
|
AUX_IRQ_STATUS_AUX_RPLY_TOUT |
|
||||||
|
AUX_IRQ_STATUS_AUX_SHORT);
|
||||||
|
|
||||||
|
regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val | AUX_CMD_SEND);
|
||||||
|
|
||||||
|
/* Zero delay loop because i2c transactions are slow already */
|
||||||
|
ret = regmap_read_poll_timeout(pdata->regmap, SN_AUX_CMD_REG, val,
|
||||||
|
!(val & AUX_CMD_SEND), 0, 50 * 1000);
|
||||||
|
if (ret)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
ret = regmap_read(pdata->regmap, SN_AUX_CMD_STATUS_REG, &val);
|
||||||
|
if (ret)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (val & AUX_IRQ_STATUS_AUX_RPLY_TOUT) {
|
||||||
|
/*
|
||||||
|
* The hardware tried the message seven times per the DP spec
|
||||||
|
* but it hit a timeout. We ignore defers here because they're
|
||||||
|
* handled in hardware.
|
||||||
|
*/
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val & AUX_IRQ_STATUS_AUX_SHORT) {
|
||||||
|
ret = regmap_read(pdata->regmap, SN_AUX_LENGTH_REG, &len);
|
||||||
|
if (ret)
|
||||||
|
goto exit;
|
||||||
|
} else if (val & AUX_IRQ_STATUS_NAT_I2C_FAIL) {
|
||||||
|
switch (request) {
|
||||||
|
case DP_AUX_I2C_WRITE:
|
||||||
|
case DP_AUX_I2C_READ:
|
||||||
|
msg->reply |= DP_AUX_I2C_REPLY_NACK;
|
||||||
|
break;
|
||||||
|
case DP_AUX_NATIVE_READ:
|
||||||
|
case DP_AUX_NATIVE_WRITE:
|
||||||
|
msg->reply |= DP_AUX_NATIVE_REPLY_NACK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len = 0;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request != DP_AUX_NATIVE_WRITE && request != DP_AUX_I2C_WRITE && len != 0)
|
||||||
|
ret = regmap_bulk_read(pdata->regmap, SN_AUX_RDATA_REG(0), buf, len);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
mutex_unlock(&pdata->comms_mutex);
|
||||||
|
pm_runtime_mark_last_busy(pdata->dev);
|
||||||
|
pm_runtime_put_autosuspend(pdata->dev);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ti_sn_aux_probe(struct auxiliary_device *adev,
|
||||||
|
const struct auxiliary_device_id *id)
|
||||||
|
{
|
||||||
|
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pdata->aux.name = "ti-sn65dsi86-aux";
|
||||||
|
pdata->aux.dev = &adev->dev;
|
||||||
|
pdata->aux.transfer = ti_sn_aux_transfer;
|
||||||
|
drm_dp_aux_init(&pdata->aux);
|
||||||
|
|
||||||
|
ret = devm_of_dp_aux_populate_ep_devices(&pdata->aux);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The eDP to MIPI bridge parts don't work until the AUX channel is
|
||||||
|
* setup so we don't add it in the main driver probe, we add it now.
|
||||||
|
*/
|
||||||
|
return ti_sn65dsi86_add_aux_device(pdata, &pdata->bridge_aux, "bridge");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct auxiliary_device_id ti_sn_aux_id_table[] = {
|
||||||
|
{ .name = "ti_sn65dsi86.aux", },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct auxiliary_driver ti_sn_aux_driver = {
|
||||||
|
.name = "aux",
|
||||||
|
.probe = ti_sn_aux_probe,
|
||||||
|
.id_table = ti_sn_aux_id_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* DRM Connector Operations
|
||||||
|
*/
|
||||||
|
|
||||||
static struct ti_sn65dsi86 *
|
static struct ti_sn65dsi86 *
|
||||||
connector_to_ti_sn65dsi86(struct drm_connector *connector)
|
connector_to_ti_sn65dsi86(struct drm_connector *connector)
|
||||||
{
|
{
|
||||||
|
@ -432,25 +636,15 @@ static const struct drm_connector_funcs ti_sn_bridge_connector_funcs = {
|
||||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* DRM Bridge
|
||||||
|
*/
|
||||||
|
|
||||||
static struct ti_sn65dsi86 *bridge_to_ti_sn65dsi86(struct drm_bridge *bridge)
|
static struct ti_sn65dsi86 *bridge_to_ti_sn65dsi86(struct drm_bridge *bridge)
|
||||||
{
|
{
|
||||||
return container_of(bridge, struct ti_sn65dsi86, bridge);
|
return container_of(bridge, struct ti_sn65dsi86, bridge);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ti_sn65dsi86_parse_regulators(struct ti_sn65dsi86 *pdata)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
const char * const ti_sn_bridge_supply_names[] = {
|
|
||||||
"vcca", "vcc", "vccio", "vpll",
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i = 0; i < SN_REGULATOR_SUPPLY_NUM; i++)
|
|
||||||
pdata->supplies[i].supply = ti_sn_bridge_supply_names[i];
|
|
||||||
|
|
||||||
return devm_regulator_bulk_get(pdata->dev, SN_REGULATOR_SUPPLY_NUM,
|
|
||||||
pdata->supplies);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ti_sn_bridge_attach(struct drm_bridge *bridge,
|
static int ti_sn_bridge_attach(struct drm_bridge *bridge,
|
||||||
enum drm_bridge_attach_flags flags)
|
enum drm_bridge_attach_flags flags)
|
||||||
{
|
{
|
||||||
|
@ -916,121 +1110,53 @@ static const struct drm_bridge_funcs ti_sn_bridge_funcs = {
|
||||||
.post_disable = ti_sn_bridge_post_disable,
|
.post_disable = ti_sn_bridge_post_disable,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct ti_sn65dsi86 *aux_to_ti_sn65dsi86(struct drm_dp_aux *aux)
|
static void ti_sn_bridge_parse_lanes(struct ti_sn65dsi86 *pdata,
|
||||||
|
struct device_node *np)
|
||||||
{
|
{
|
||||||
return container_of(aux, struct ti_sn65dsi86, aux);
|
u32 lane_assignments[SN_MAX_DP_LANES] = { 0, 1, 2, 3 };
|
||||||
}
|
u32 lane_polarities[SN_MAX_DP_LANES] = { };
|
||||||
|
struct device_node *endpoint;
|
||||||
static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,
|
u8 ln_assign = 0;
|
||||||
struct drm_dp_aux_msg *msg)
|
u8 ln_polrs = 0;
|
||||||
{
|
int dp_lanes;
|
||||||
struct ti_sn65dsi86 *pdata = aux_to_ti_sn65dsi86(aux);
|
int i;
|
||||||
u32 request = msg->request & ~(DP_AUX_I2C_MOT | DP_AUX_I2C_WRITE_STATUS_UPDATE);
|
|
||||||
u32 request_val = AUX_CMD_REQ(msg->request);
|
|
||||||
u8 *buf = msg->buffer;
|
|
||||||
unsigned int len = msg->size;
|
|
||||||
unsigned int val;
|
|
||||||
int ret;
|
|
||||||
u8 addr_len[SN_AUX_LENGTH_REG + 1 - SN_AUX_ADDR_19_16_REG];
|
|
||||||
|
|
||||||
if (len > SN_AUX_MAX_PAYLOAD_BYTES)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
pm_runtime_get_sync(pdata->dev);
|
|
||||||
mutex_lock(&pdata->comms_mutex);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If someone tries to do a DDC over AUX transaction before pre_enable()
|
* Read config from the device tree about lane remapping and lane
|
||||||
* on a device without a dedicated reference clock then we just can't
|
* polarities. These are optional and we assume identity map and
|
||||||
* do it. Fail right away. This prevents non-refclk users from reading
|
* normal polarity if nothing is specified. It's OK to specify just
|
||||||
* the EDID before enabling the panel but such is life.
|
* data-lanes but not lane-polarities but not vice versa.
|
||||||
|
*
|
||||||
|
* Error checking is light (we just make sure we don't crash or
|
||||||
|
* buffer overrun) and we assume dts is well formed and specifying
|
||||||
|
* mappings that the hardware supports.
|
||||||
*/
|
*/
|
||||||
if (!pdata->comms_enabled) {
|
endpoint = of_graph_get_endpoint_by_regs(np, 1, -1);
|
||||||
ret = -EIO;
|
dp_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
|
||||||
goto exit;
|
if (dp_lanes > 0 && dp_lanes <= SN_MAX_DP_LANES) {
|
||||||
|
of_property_read_u32_array(endpoint, "data-lanes",
|
||||||
|
lane_assignments, dp_lanes);
|
||||||
|
of_property_read_u32_array(endpoint, "lane-polarities",
|
||||||
|
lane_polarities, dp_lanes);
|
||||||
|
} else {
|
||||||
|
dp_lanes = SN_MAX_DP_LANES;
|
||||||
|
}
|
||||||
|
of_node_put(endpoint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert into register format. Loop over all lanes even if
|
||||||
|
* data-lanes had fewer elements so that we nicely initialize
|
||||||
|
* the LN_ASSIGN register.
|
||||||
|
*/
|
||||||
|
for (i = SN_MAX_DP_LANES - 1; i >= 0; i--) {
|
||||||
|
ln_assign = ln_assign << LN_ASSIGN_WIDTH | lane_assignments[i];
|
||||||
|
ln_polrs = ln_polrs << 1 | lane_polarities[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (request) {
|
/* Stash in our struct for when we power on */
|
||||||
case DP_AUX_NATIVE_WRITE:
|
pdata->dp_lanes = dp_lanes;
|
||||||
case DP_AUX_I2C_WRITE:
|
pdata->ln_assign = ln_assign;
|
||||||
case DP_AUX_NATIVE_READ:
|
pdata->ln_polrs = ln_polrs;
|
||||||
case DP_AUX_I2C_READ:
|
|
||||||
regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val);
|
|
||||||
/* Assume it's good */
|
|
||||||
msg->reply = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
BUILD_BUG_ON(sizeof(addr_len) != sizeof(__be32));
|
|
||||||
put_unaligned_be32((msg->address & SN_AUX_ADDR_MASK) << 8 | len,
|
|
||||||
addr_len);
|
|
||||||
regmap_bulk_write(pdata->regmap, SN_AUX_ADDR_19_16_REG, addr_len,
|
|
||||||
ARRAY_SIZE(addr_len));
|
|
||||||
|
|
||||||
if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE)
|
|
||||||
regmap_bulk_write(pdata->regmap, SN_AUX_WDATA_REG(0), buf, len);
|
|
||||||
|
|
||||||
/* Clear old status bits before start so we don't get confused */
|
|
||||||
regmap_write(pdata->regmap, SN_AUX_CMD_STATUS_REG,
|
|
||||||
AUX_IRQ_STATUS_NAT_I2C_FAIL |
|
|
||||||
AUX_IRQ_STATUS_AUX_RPLY_TOUT |
|
|
||||||
AUX_IRQ_STATUS_AUX_SHORT);
|
|
||||||
|
|
||||||
regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val | AUX_CMD_SEND);
|
|
||||||
|
|
||||||
/* Zero delay loop because i2c transactions are slow already */
|
|
||||||
ret = regmap_read_poll_timeout(pdata->regmap, SN_AUX_CMD_REG, val,
|
|
||||||
!(val & AUX_CMD_SEND), 0, 50 * 1000);
|
|
||||||
if (ret)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
ret = regmap_read(pdata->regmap, SN_AUX_CMD_STATUS_REG, &val);
|
|
||||||
if (ret)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
if (val & AUX_IRQ_STATUS_AUX_RPLY_TOUT) {
|
|
||||||
/*
|
|
||||||
* The hardware tried the message seven times per the DP spec
|
|
||||||
* but it hit a timeout. We ignore defers here because they're
|
|
||||||
* handled in hardware.
|
|
||||||
*/
|
|
||||||
ret = -ETIMEDOUT;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val & AUX_IRQ_STATUS_AUX_SHORT) {
|
|
||||||
ret = regmap_read(pdata->regmap, SN_AUX_LENGTH_REG, &len);
|
|
||||||
if (ret)
|
|
||||||
goto exit;
|
|
||||||
} else if (val & AUX_IRQ_STATUS_NAT_I2C_FAIL) {
|
|
||||||
switch (request) {
|
|
||||||
case DP_AUX_I2C_WRITE:
|
|
||||||
case DP_AUX_I2C_READ:
|
|
||||||
msg->reply |= DP_AUX_I2C_REPLY_NACK;
|
|
||||||
break;
|
|
||||||
case DP_AUX_NATIVE_READ:
|
|
||||||
case DP_AUX_NATIVE_WRITE:
|
|
||||||
msg->reply |= DP_AUX_NATIVE_REPLY_NACK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
len = 0;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request != DP_AUX_NATIVE_WRITE && request != DP_AUX_I2C_WRITE && len != 0)
|
|
||||||
ret = regmap_bulk_read(pdata->regmap, SN_AUX_RDATA_REG(0), buf, len);
|
|
||||||
|
|
||||||
exit:
|
|
||||||
mutex_unlock(&pdata->comms_mutex);
|
|
||||||
pm_runtime_mark_last_busy(pdata->dev);
|
|
||||||
pm_runtime_put_autosuspend(pdata->dev);
|
|
||||||
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ti_sn_bridge_parse_dsi_host(struct ti_sn65dsi86 *pdata)
|
static int ti_sn_bridge_parse_dsi_host(struct ti_sn65dsi86 *pdata)
|
||||||
|
@ -1047,6 +1173,72 @@ static int ti_sn_bridge_parse_dsi_host(struct ti_sn65dsi86 *pdata)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ti_sn_bridge_probe(struct auxiliary_device *adev,
|
||||||
|
const struct auxiliary_device_id *id)
|
||||||
|
{
|
||||||
|
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
|
||||||
|
struct device_node *np = pdata->dev->of_node;
|
||||||
|
struct drm_panel *panel;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL);
|
||||||
|
if (ret)
|
||||||
|
return dev_err_probe(&adev->dev, ret,
|
||||||
|
"could not find any panel node\n");
|
||||||
|
|
||||||
|
pdata->next_bridge = devm_drm_panel_bridge_add(pdata->dev, panel);
|
||||||
|
if (IS_ERR(pdata->next_bridge)) {
|
||||||
|
DRM_ERROR("failed to create panel bridge\n");
|
||||||
|
return PTR_ERR(pdata->next_bridge);
|
||||||
|
}
|
||||||
|
|
||||||
|
ti_sn_bridge_parse_lanes(pdata, np);
|
||||||
|
|
||||||
|
ret = ti_sn_bridge_parse_dsi_host(pdata);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
pdata->bridge.funcs = &ti_sn_bridge_funcs;
|
||||||
|
pdata->bridge.of_node = np;
|
||||||
|
|
||||||
|
drm_bridge_add(&pdata->bridge);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ti_sn_bridge_remove(struct auxiliary_device *adev)
|
||||||
|
{
|
||||||
|
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
|
||||||
|
|
||||||
|
if (!pdata)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pdata->dsi) {
|
||||||
|
mipi_dsi_detach(pdata->dsi);
|
||||||
|
mipi_dsi_device_unregister(pdata->dsi);
|
||||||
|
}
|
||||||
|
|
||||||
|
drm_bridge_remove(&pdata->bridge);
|
||||||
|
|
||||||
|
of_node_put(pdata->host_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct auxiliary_device_id ti_sn_bridge_id_table[] = {
|
||||||
|
{ .name = "ti_sn65dsi86.bridge", },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct auxiliary_driver ti_sn_bridge_driver = {
|
||||||
|
.name = "bridge",
|
||||||
|
.probe = ti_sn_bridge_probe,
|
||||||
|
.remove = ti_sn_bridge_remove,
|
||||||
|
.id_table = ti_sn_bridge_id_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* GPIO Controller
|
||||||
|
*/
|
||||||
|
|
||||||
#if defined(CONFIG_OF_GPIO)
|
#if defined(CONFIG_OF_GPIO)
|
||||||
|
|
||||||
static int tn_sn_bridge_of_xlate(struct gpio_chip *chip,
|
static int tn_sn_bridge_of_xlate(struct gpio_chip *chip,
|
||||||
|
@ -1251,198 +1443,29 @@ static inline void ti_sn_gpio_unregister(void) {}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void ti_sn_bridge_parse_lanes(struct ti_sn65dsi86 *pdata,
|
/* -----------------------------------------------------------------------------
|
||||||
struct device_node *np)
|
* Probe & Remove
|
||||||
{
|
*/
|
||||||
u32 lane_assignments[SN_MAX_DP_LANES] = { 0, 1, 2, 3 };
|
|
||||||
u32 lane_polarities[SN_MAX_DP_LANES] = { };
|
|
||||||
struct device_node *endpoint;
|
|
||||||
u8 ln_assign = 0;
|
|
||||||
u8 ln_polrs = 0;
|
|
||||||
int dp_lanes;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read config from the device tree about lane remapping and lane
|
|
||||||
* polarities. These are optional and we assume identity map and
|
|
||||||
* normal polarity if nothing is specified. It's OK to specify just
|
|
||||||
* data-lanes but not lane-polarities but not vice versa.
|
|
||||||
*
|
|
||||||
* Error checking is light (we just make sure we don't crash or
|
|
||||||
* buffer overrun) and we assume dts is well formed and specifying
|
|
||||||
* mappings that the hardware supports.
|
|
||||||
*/
|
|
||||||
endpoint = of_graph_get_endpoint_by_regs(np, 1, -1);
|
|
||||||
dp_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
|
|
||||||
if (dp_lanes > 0 && dp_lanes <= SN_MAX_DP_LANES) {
|
|
||||||
of_property_read_u32_array(endpoint, "data-lanes",
|
|
||||||
lane_assignments, dp_lanes);
|
|
||||||
of_property_read_u32_array(endpoint, "lane-polarities",
|
|
||||||
lane_polarities, dp_lanes);
|
|
||||||
} else {
|
|
||||||
dp_lanes = SN_MAX_DP_LANES;
|
|
||||||
}
|
|
||||||
of_node_put(endpoint);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert into register format. Loop over all lanes even if
|
|
||||||
* data-lanes had fewer elements so that we nicely initialize
|
|
||||||
* the LN_ASSIGN register.
|
|
||||||
*/
|
|
||||||
for (i = SN_MAX_DP_LANES - 1; i >= 0; i--) {
|
|
||||||
ln_assign = ln_assign << LN_ASSIGN_WIDTH | lane_assignments[i];
|
|
||||||
ln_polrs = ln_polrs << 1 | lane_polarities[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stash in our struct for when we power on */
|
|
||||||
pdata->dp_lanes = dp_lanes;
|
|
||||||
pdata->ln_assign = ln_assign;
|
|
||||||
pdata->ln_polrs = ln_polrs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ti_sn_bridge_probe(struct auxiliary_device *adev,
|
|
||||||
const struct auxiliary_device_id *id)
|
|
||||||
{
|
|
||||||
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
|
|
||||||
struct device_node *np = pdata->dev->of_node;
|
|
||||||
struct drm_panel *panel;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL);
|
|
||||||
if (ret)
|
|
||||||
return dev_err_probe(&adev->dev, ret,
|
|
||||||
"could not find any panel node\n");
|
|
||||||
|
|
||||||
pdata->next_bridge = devm_drm_panel_bridge_add(pdata->dev, panel);
|
|
||||||
if (IS_ERR(pdata->next_bridge)) {
|
|
||||||
DRM_ERROR("failed to create panel bridge\n");
|
|
||||||
return PTR_ERR(pdata->next_bridge);
|
|
||||||
}
|
|
||||||
|
|
||||||
ti_sn_bridge_parse_lanes(pdata, np);
|
|
||||||
|
|
||||||
ret = ti_sn_bridge_parse_dsi_host(pdata);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
pdata->bridge.funcs = &ti_sn_bridge_funcs;
|
|
||||||
pdata->bridge.of_node = np;
|
|
||||||
|
|
||||||
drm_bridge_add(&pdata->bridge);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ti_sn_bridge_remove(struct auxiliary_device *adev)
|
|
||||||
{
|
|
||||||
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
|
|
||||||
|
|
||||||
if (!pdata)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (pdata->dsi) {
|
|
||||||
mipi_dsi_detach(pdata->dsi);
|
|
||||||
mipi_dsi_device_unregister(pdata->dsi);
|
|
||||||
}
|
|
||||||
|
|
||||||
drm_bridge_remove(&pdata->bridge);
|
|
||||||
|
|
||||||
of_node_put(pdata->host_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct auxiliary_device_id ti_sn_bridge_id_table[] = {
|
|
||||||
{ .name = "ti_sn65dsi86.bridge", },
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct auxiliary_driver ti_sn_bridge_driver = {
|
|
||||||
.name = "bridge",
|
|
||||||
.probe = ti_sn_bridge_probe,
|
|
||||||
.remove = ti_sn_bridge_remove,
|
|
||||||
.id_table = ti_sn_bridge_id_table,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ti_sn65dsi86_runtime_disable(void *data)
|
static void ti_sn65dsi86_runtime_disable(void *data)
|
||||||
{
|
{
|
||||||
pm_runtime_disable(data);
|
pm_runtime_disable(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ti_sn65dsi86_uninit_aux(void *data)
|
static int ti_sn65dsi86_parse_regulators(struct ti_sn65dsi86 *pdata)
|
||||||
{
|
{
|
||||||
auxiliary_device_uninit(data);
|
unsigned int i;
|
||||||
|
const char * const ti_sn_bridge_supply_names[] = {
|
||||||
|
"vcca", "vcc", "vccio", "vpll",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < SN_REGULATOR_SUPPLY_NUM; i++)
|
||||||
|
pdata->supplies[i].supply = ti_sn_bridge_supply_names[i];
|
||||||
|
|
||||||
|
return devm_regulator_bulk_get(pdata->dev, SN_REGULATOR_SUPPLY_NUM,
|
||||||
|
pdata->supplies);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ti_sn65dsi86_delete_aux(void *data)
|
|
||||||
{
|
|
||||||
auxiliary_device_delete(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* AUX bus docs say that a non-NULL release is mandatory, but it makes no
|
|
||||||
* sense for the model used here where all of the aux devices are allocated
|
|
||||||
* in the single shared structure. We'll use this noop as a workaround.
|
|
||||||
*/
|
|
||||||
static void ti_sn65dsi86_noop(struct device *dev) {}
|
|
||||||
|
|
||||||
static int ti_sn65dsi86_add_aux_device(struct ti_sn65dsi86 *pdata,
|
|
||||||
struct auxiliary_device *aux,
|
|
||||||
const char *name)
|
|
||||||
{
|
|
||||||
struct device *dev = pdata->dev;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
aux->name = name;
|
|
||||||
aux->dev.parent = dev;
|
|
||||||
aux->dev.release = ti_sn65dsi86_noop;
|
|
||||||
device_set_of_node_from_dev(&aux->dev, dev);
|
|
||||||
ret = auxiliary_device_init(aux);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
ret = devm_add_action_or_reset(dev, ti_sn65dsi86_uninit_aux, aux);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = auxiliary_device_add(aux);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
ret = devm_add_action_or_reset(dev, ti_sn65dsi86_delete_aux, aux);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ti_sn_aux_probe(struct auxiliary_device *adev,
|
|
||||||
const struct auxiliary_device_id *id)
|
|
||||||
{
|
|
||||||
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
pdata->aux.name = "ti-sn65dsi86-aux";
|
|
||||||
pdata->aux.dev = &adev->dev;
|
|
||||||
pdata->aux.transfer = ti_sn_aux_transfer;
|
|
||||||
drm_dp_aux_init(&pdata->aux);
|
|
||||||
|
|
||||||
ret = devm_of_dp_aux_populate_ep_devices(&pdata->aux);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The eDP to MIPI bridge parts don't work until the AUX channel is
|
|
||||||
* setup so we don't add it in the main driver probe, we add it now.
|
|
||||||
*/
|
|
||||||
return ti_sn65dsi86_add_aux_device(pdata, &pdata->bridge_aux, "bridge");
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct auxiliary_device_id ti_sn_aux_id_table[] = {
|
|
||||||
{ .name = "ti_sn65dsi86.aux", },
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct auxiliary_driver ti_sn_aux_driver = {
|
|
||||||
.name = "aux",
|
|
||||||
.probe = ti_sn_aux_probe,
|
|
||||||
.id_table = ti_sn_aux_id_table,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int ti_sn65dsi86_probe(struct i2c_client *client,
|
static int ti_sn65dsi86_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue