aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c/adv748x
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/adv748x')
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c8
-rw-r--r--drivers/media/i2c/adv748x/adv748x-core.c93
-rw-r--r--drivers/media/i2c/adv748x/adv748x-csi2.c35
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c8
-rw-r--r--drivers/media/i2c/adv748x/adv748x.h25
5 files changed, 75 insertions, 94 deletions
diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c
index edd25e895e5d..71714634efb0 100644
--- a/drivers/media/i2c/adv748x/adv748x-afe.c
+++ b/drivers/media/i2c/adv748x/adv748x-afe.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Analog Devices ADV748X 8 channel analog front end (AFE) receiver
* with standard definition processor (SDP)
*
* Copyright (C) 2017 Renesas Electronics Corp.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/delay.h>
@@ -286,7 +282,7 @@ static int adv748x_afe_s_stream(struct v4l2_subdev *sd, int enable)
goto unlock;
}
- ret = adv748x_txb_power(state, enable);
+ ret = adv748x_tx_power(&state->txb, enable);
if (ret)
goto unlock;
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
index 6ca88daa0ecd..6854d898fdd1 100644
--- a/drivers/media/i2c/adv748x/adv748x-core.c
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Analog Devices ADV748X HDMI receiver with AFE
*
* Copyright (C) 2017 Renesas Electronics Corp.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
* Authors:
* Koji Matsuoka <koji.matsuoka.xm@renesas.com>
* Niklas Söderlund <niklas.soderlund@ragnatech.se>
@@ -285,40 +281,23 @@ static const struct adv748x_reg_value adv748x_power_down_txb_1lane[] = {
{ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */
{ADV748X_PAGE_TXB, 0x1e, 0x00}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 4-lane MIPI */
+ {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */
{ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
{ADV748X_PAGE_TXB, 0xc1, 0x3b}, /* ADI Required Write */
{ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
};
-int adv748x_txa_power(struct adv748x_state *state, bool on)
+int adv748x_tx_power(struct adv748x_csi2 *tx, bool on)
{
+ struct adv748x_state *state = tx->state;
+ const struct adv748x_reg_value *reglist;
int val;
- val = txa_read(state, ADV748X_CSI_FS_AS_LS);
- if (val < 0)
- return val;
-
- /*
- * This test against BIT(6) is not documented by the datasheet, but was
- * specified in the downstream driver.
- * Track with a WARN_ONCE to determine if it is ever set by HW.
- */
- WARN_ONCE((on && val & ADV748X_CSI_FS_AS_LS_UNKNOWN),
- "Enabling with unknown bit set");
+ if (!is_tx_enabled(tx))
+ return 0;
- if (on)
- return adv748x_write_regs(state, adv748x_power_up_txa_4lane);
-
- return adv748x_write_regs(state, adv748x_power_down_txa_4lane);
-}
-
-int adv748x_txb_power(struct adv748x_state *state, bool on)
-{
- int val;
-
- val = txb_read(state, ADV748X_CSI_FS_AS_LS);
+ val = tx_read(tx, ADV748X_CSI_FS_AS_LS);
if (val < 0)
return val;
@@ -331,9 +310,13 @@ int adv748x_txb_power(struct adv748x_state *state, bool on)
"Enabling with unknown bit set");
if (on)
- return adv748x_write_regs(state, adv748x_power_up_txb_1lane);
+ reglist = is_txa(tx) ? adv748x_power_up_txa_4lane :
+ adv748x_power_up_txb_1lane;
+ else
+ reglist = is_txa(tx) ? adv748x_power_down_txa_4lane :
+ adv748x_power_down_txb_1lane;
- return adv748x_write_regs(state, adv748x_power_down_txb_1lane);
+ return adv748x_write_regs(state, reglist);
}
/* -----------------------------------------------------------------------------
@@ -399,8 +382,6 @@ static const struct adv748x_reg_value adv748x_init_txa_4lane[] = {
{ADV748X_PAGE_IO, 0x0c, 0xe0}, /* Enable LLC_DLL & Double LLC Timing */
{ADV748X_PAGE_IO, 0x0e, 0xdd}, /* LLC/PIX/SPI PINS TRISTATED AUD */
- /* Outputs Enabled */
- {ADV748X_PAGE_IO, 0x10, 0xa0}, /* Enable 4-lane CSI Tx & Pixel Port */
{ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
{ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */
@@ -454,10 +435,6 @@ static const struct adv748x_reg_value adv748x_init_txb_1lane[] = {
{ADV748X_PAGE_SDP, 0x31, 0x12}, /* ADI Required Write */
{ADV748X_PAGE_SDP, 0xe6, 0x4f}, /* V bit end pos manually in NTSC */
- /* Enable 1-Lane MIPI Tx, */
- /* enable pixel output and route SD through Pixel port */
- {ADV748X_PAGE_IO, 0x10, 0x70},
-
{ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */
{ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */
{ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */
@@ -482,6 +459,7 @@ static const struct adv748x_reg_value adv748x_init_txb_1lane[] = {
static int adv748x_reset(struct adv748x_state *state)
{
int ret;
+ u8 regval = 0;
ret = adv748x_write_regs(state, adv748x_sw_reset);
if (ret < 0)
@@ -496,22 +474,24 @@ static int adv748x_reset(struct adv748x_state *state)
if (ret)
return ret;
- adv748x_txa_power(state, 0);
+ adv748x_tx_power(&state->txa, 0);
/* Init and power down TXB */
ret = adv748x_write_regs(state, adv748x_init_txb_1lane);
if (ret)
return ret;
- adv748x_txb_power(state, 0);
+ adv748x_tx_power(&state->txb, 0);
/* Disable chip powerdown & Enable HDMI Rx block */
io_write(state, ADV748X_IO_PD, ADV748X_IO_PD_RX_EN);
- /* Enable 4-lane CSI Tx & Pixel Port */
- io_write(state, ADV748X_IO_10, ADV748X_IO_10_CSI4_EN |
- ADV748X_IO_10_CSI1_EN |
- ADV748X_IO_10_PIX_OUT_EN);
+ /* Conditionally enable TXa and TXb. */
+ if (is_tx_enabled(&state->txa))
+ regval |= ADV748X_IO_10_CSI4_EN;
+ if (is_tx_enabled(&state->txb))
+ regval |= ADV748X_IO_10_CSI1_EN;
+ io_write(state, ADV748X_IO_10, regval);
/* Use vid_std and v_freq as freerun resolution for CP */
cp_clrset(state, ADV748X_CP_CLMP_POS, ADV748X_CP_CLMP_POS_DIS_AUTO,
@@ -569,7 +549,8 @@ static int adv748x_parse_dt(struct adv748x_state *state)
{
struct device_node *ep_np = NULL;
struct of_endpoint ep;
- bool found = false;
+ bool out_found = false;
+ bool in_found = false;
for_each_endpoint_of_node(state->dev->of_node, ep_np) {
of_graph_parse_endpoint(ep_np, &ep);
@@ -592,10 +573,17 @@ static int adv748x_parse_dt(struct adv748x_state *state)
of_node_get(ep_np);
state->endpoints[ep.port] = ep_np;
- found = true;
+ /*
+ * At least one input endpoint and one output endpoint shall
+ * be defined.
+ */
+ if (ep.port < ADV748X_PORT_TXA)
+ in_found = true;
+ else
+ out_found = true;
}
- return found ? 0 : -ENODEV;
+ return in_found && out_found ? 0 : -ENODEV;
}
static void adv748x_dt_cleanup(struct adv748x_state *state)
@@ -627,6 +615,17 @@ static int adv748x_probe(struct i2c_client *client,
state->i2c_clients[ADV748X_PAGE_IO] = client;
i2c_set_clientdata(client, state);
+ /*
+ * We can not use container_of to get back to the state with two TXs;
+ * Initialize the TXs's fields unconditionally on the endpoint
+ * presence to access them later.
+ */
+ state->txa.state = state->txb.state = state;
+ state->txa.page = ADV748X_PAGE_TXA;
+ state->txb.page = ADV748X_PAGE_TXB;
+ state->txa.port = ADV748X_PORT_TXA;
+ state->txb.port = ADV748X_PORT_TXB;
+
/* Discover and process ports declared by the Device tree endpoints */
ret = adv748x_parse_dt(state);
if (ret) {
@@ -755,4 +754,4 @@ module_i2c_driver(adv748x_driver);
MODULE_AUTHOR("Kieran Bingham <kieran.bingham@ideasonboard.com>");
MODULE_DESCRIPTION("ADV748X video decoder");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c
index 469be87a3761..6ce21542ed48 100644
--- a/drivers/media/i2c/adv748x/adv748x-csi2.c
+++ b/drivers/media/i2c/adv748x/adv748x-csi2.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Analog Devices ADV748X CSI-2 Transmitter
*
* Copyright (C) 2017 Renesas Electronics Corp.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/module.h>
@@ -18,11 +14,6 @@
#include "adv748x.h"
-static bool is_txa(struct adv748x_csi2 *tx)
-{
- return tx == &tx->state->txa;
-}
-
static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx,
unsigned int vc)
{
@@ -87,15 +78,15 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd)
*
* Link HDMI->TXA, and AFE->TXB directly.
*/
- if (is_txa(tx)) {
+ if (is_txa(tx) && is_hdmi_enabled(state))
return adv748x_csi2_register_link(tx, sd->v4l2_dev,
&state->hdmi.sd,
ADV748X_HDMI_SOURCE);
- } else {
+ if (!is_txa(tx) && is_afe_enabled(state))
return adv748x_csi2_register_link(tx, sd->v4l2_dev,
&state->afe.sd,
ADV748X_AFE_SOURCE);
- }
+ return 0;
}
static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = {
@@ -266,19 +257,10 @@ static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx)
int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
{
- struct device_node *ep;
int ret;
- /* We can not use container_of to get back to the state with two TXs */
- tx->state = state;
- tx->page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB;
-
- ep = state->endpoints[is_txa(tx) ? ADV748X_PORT_TXA : ADV748X_PORT_TXB];
- if (!ep) {
- adv_err(state, "No endpoint found for %s\n",
- is_txa(tx) ? "txa" : "txb");
- return -ENODEV;
- }
+ if (!is_tx_enabled(tx))
+ return 0;
/* Initialise the virtual channel */
adv748x_csi2_set_virtual_channel(tx, 0);
@@ -288,7 +270,7 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
is_txa(tx) ? "txa" : "txb");
/* Ensure that matching is based upon the endpoint fwnodes */
- tx->sd.fwnode = of_fwnode_handle(ep);
+ tx->sd.fwnode = of_fwnode_handle(state->endpoints[tx->port]);
/* Register internal ops for incremental subdev registration */
tx->sd.internal_ops = &adv748x_csi2_internal_ops;
@@ -321,6 +303,9 @@ err_free_media:
void adv748x_csi2_cleanup(struct adv748x_csi2 *tx)
{
+ if (!is_tx_enabled(tx))
+ return;
+
v4l2_async_unregister_subdev(&tx->sd);
media_entity_cleanup(&tx->sd.entity);
v4l2_ctrl_handler_free(&tx->ctrl_hdl);
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
index aecc2a84dfec..35d027941482 100644
--- a/drivers/media/i2c/adv748x/adv748x-hdmi.c
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Analog Devices ADV748X HDMI receiver and Component Processor (CP)
*
* Copyright (C) 2017 Renesas Electronics Corp.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/module.h>
@@ -362,7 +358,7 @@ static int adv748x_hdmi_s_stream(struct v4l2_subdev *sd, int enable)
mutex_lock(&state->mutex);
- ret = adv748x_txa_power(state, enable);
+ ret = adv748x_tx_power(&state->txa, enable);
if (ret)
goto done;
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
index 65f83741277e..39c2fdc3b416 100644
--- a/drivers/media/i2c/adv748x/adv748x.h
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -1,13 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Driver for Analog Devices ADV748X video decoder and HDMI receiver
*
* Copyright (C) 2017 Renesas Electronics Corp.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
* Authors:
* Koji Matsuoka <koji.matsuoka.xm@renesas.com>
* Niklas Söderlund <niklas.soderlund@ragnatech.se>
@@ -82,6 +78,7 @@ struct adv748x_csi2 {
struct adv748x_state *state;
struct v4l2_mbus_framefmt format;
unsigned int page;
+ unsigned int port;
struct media_pad pads[ADV748X_CSI2_NR_PADS];
struct v4l2_ctrl_handler ctrl_hdl;
@@ -91,6 +88,18 @@ struct adv748x_csi2 {
#define notifier_to_csi2(n) container_of(n, struct adv748x_csi2, notifier)
#define adv748x_sd_to_csi2(sd) container_of(sd, struct adv748x_csi2, sd)
+#define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL)
+#define is_txa(_tx) ((_tx) == &(_tx)->state->txa)
+#define is_afe_enabled(_state) \
+ ((_state)->endpoints[ADV748X_PORT_AIN0] != NULL || \
+ (_state)->endpoints[ADV748X_PORT_AIN1] != NULL || \
+ (_state)->endpoints[ADV748X_PORT_AIN2] != NULL || \
+ (_state)->endpoints[ADV748X_PORT_AIN3] != NULL || \
+ (_state)->endpoints[ADV748X_PORT_AIN4] != NULL || \
+ (_state)->endpoints[ADV748X_PORT_AIN5] != NULL || \
+ (_state)->endpoints[ADV748X_PORT_AIN6] != NULL || \
+ (_state)->endpoints[ADV748X_PORT_AIN7] != NULL)
+#define is_hdmi_enabled(_state) ((_state)->endpoints[ADV748X_PORT_HDMI] != NULL)
enum adv748x_hdmi_pads {
ADV748X_HDMI_SINK,
@@ -376,9 +385,6 @@ int adv748x_write_block(struct adv748x_state *state, int client_page,
#define cp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_CP, r, v)
#define cp_clrset(s, r, m, v) cp_write(s, r, (cp_read(s, r) & ~m) | v)
-#define txa_read(s, r) adv748x_read(s, ADV748X_PAGE_TXA, r)
-#define txb_read(s, r) adv748x_read(s, ADV748X_PAGE_TXB, r)
-
#define tx_read(t, r) adv748x_read(t->state, t->page, r)
#define tx_write(t, r, v) adv748x_write(t->state, t->page, r, v)
@@ -398,8 +404,7 @@ void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state,
int adv748x_register_subdevs(struct adv748x_state *state,
struct v4l2_device *v4l2_dev);
-int adv748x_txa_power(struct adv748x_state *state, bool on);
-int adv748x_txb_power(struct adv748x_state *state, bool on);
+int adv748x_tx_power(struct adv748x_csi2 *tx, bool on);
int adv748x_afe_init(struct adv748x_afe *afe);
void adv748x_afe_cleanup(struct adv748x_afe *afe);