Commit eccf442c authored by Jacopo Mondi's avatar Jacopo Mondi Committed by Mauro Carvalho Chehab

media: i2c: adv748x: Support probing a single output

Currently the adv748x driver will fail to probe unless both of its
output endpoints (TXA and TXB) are connected.

Make the driver support probing provided that there is at least one
input, and one output connected and protect the clean-up function from
accessing un-initialized fields.

Following patches will fix other uses of un-initialized TXs in the driver,
such as power management functions.
Tested-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarJacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: default avatarKieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent bf7464a7
...@@ -565,7 +565,8 @@ static int adv748x_parse_dt(struct adv748x_state *state) ...@@ -565,7 +565,8 @@ static int adv748x_parse_dt(struct adv748x_state *state)
{ {
struct device_node *ep_np = NULL; struct device_node *ep_np = NULL;
struct of_endpoint ep; 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) { for_each_endpoint_of_node(state->dev->of_node, ep_np) {
of_graph_parse_endpoint(ep_np, &ep); of_graph_parse_endpoint(ep_np, &ep);
...@@ -588,10 +589,17 @@ static int adv748x_parse_dt(struct adv748x_state *state) ...@@ -588,10 +589,17 @@ static int adv748x_parse_dt(struct adv748x_state *state)
of_node_get(ep_np); of_node_get(ep_np);
state->endpoints[ep.port] = 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) static void adv748x_dt_cleanup(struct adv748x_state *state)
...@@ -623,6 +631,17 @@ static int adv748x_probe(struct i2c_client *client, ...@@ -623,6 +631,17 @@ static int adv748x_probe(struct i2c_client *client,
state->i2c_clients[ADV748X_PAGE_IO] = client; state->i2c_clients[ADV748X_PAGE_IO] = client;
i2c_set_clientdata(client, state); 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 */ /* Discover and process ports declared by the Device tree endpoints */
ret = adv748x_parse_dt(state); ret = adv748x_parse_dt(state);
if (ret) { if (ret) {
......
...@@ -262,19 +262,10 @@ static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx) ...@@ -262,19 +262,10 @@ static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx)
int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
{ {
struct device_node *ep;
int ret; int ret;
/* We can not use container_of to get back to the state with two TXs */ if (!is_tx_enabled(tx))
tx->state = state; return 0;
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;
}
/* Initialise the virtual channel */ /* Initialise the virtual channel */
adv748x_csi2_set_virtual_channel(tx, 0); adv748x_csi2_set_virtual_channel(tx, 0);
...@@ -284,7 +275,7 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) ...@@ -284,7 +275,7 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
is_txa(tx) ? "txa" : "txb"); is_txa(tx) ? "txa" : "txb");
/* Ensure that matching is based upon the endpoint fwnodes */ /* 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 */ /* Register internal ops for incremental subdev registration */
tx->sd.internal_ops = &adv748x_csi2_internal_ops; tx->sd.internal_ops = &adv748x_csi2_internal_ops;
...@@ -317,6 +308,9 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) ...@@ -317,6 +308,9 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
void adv748x_csi2_cleanup(struct adv748x_csi2 *tx) void adv748x_csi2_cleanup(struct adv748x_csi2 *tx)
{ {
if (!is_tx_enabled(tx))
return;
v4l2_async_unregister_subdev(&tx->sd); v4l2_async_unregister_subdev(&tx->sd);
media_entity_cleanup(&tx->sd.entity); media_entity_cleanup(&tx->sd.entity);
v4l2_ctrl_handler_free(&tx->ctrl_hdl); v4l2_ctrl_handler_free(&tx->ctrl_hdl);
......
...@@ -78,6 +78,7 @@ struct adv748x_csi2 { ...@@ -78,6 +78,7 @@ struct adv748x_csi2 {
struct adv748x_state *state; struct adv748x_state *state;
struct v4l2_mbus_framefmt format; struct v4l2_mbus_framefmt format;
unsigned int page; unsigned int page;
unsigned int port;
struct media_pad pads[ADV748X_CSI2_NR_PADS]; struct media_pad pads[ADV748X_CSI2_NR_PADS];
struct v4l2_ctrl_handler ctrl_hdl; struct v4l2_ctrl_handler ctrl_hdl;
...@@ -87,6 +88,7 @@ struct adv748x_csi2 { ...@@ -87,6 +88,7 @@ struct adv748x_csi2 {
#define notifier_to_csi2(n) container_of(n, struct adv748x_csi2, notifier) #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 adv748x_sd_to_csi2(sd) container_of(sd, struct adv748x_csi2, sd)
#define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL)
enum adv748x_hdmi_pads { enum adv748x_hdmi_pads {
ADV748X_HDMI_SINK, ADV748X_HDMI_SINK,
......
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