Commit 25614824 authored by Philipp Zabel's avatar Philipp Zabel Committed by Mauro Carvalho Chehab

[media] tc358743: support probe from device tree

Add support for probing the TC358743 subdevice from device tree.
The reference clock must be supplied using the common clock bindings.
MIPI CSI-2 specific properties are parsed from the OF graph endpoint
node and support for a non-continuous MIPI CSI-2 clock is added.
Signed-off-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent 8ec23da7
* Toshiba TC358743 HDMI-RX to MIPI CSI2-TX Bridge
The Toshiba TC358743 HDMI-RX to MIPI CSI2-TX (H2C) is a bridge that converts
a HDMI stream to MIPI CSI-2 TX. It is programmable through I2C.
Required Properties:
- compatible: value should be "toshiba,tc358743"
- clocks, clock-names: should contain a phandle link to the reference clock
source, the clock input is named "refclk".
Optional Properties:
- reset-gpios: gpio phandle GPIO connected to the reset pin
- interrupts, interrupt-parent: GPIO connected to the interrupt pin
- data-lanes: should be <1 2 3 4> for four-lane operation,
or <1 2> for two-lane operation
- clock-lanes: should be <0>
- clock-noncontinuous: Presence of this boolean property decides whether the
MIPI CSI-2 clock is continuous or non-continuous.
- link-frequencies: List of allowed link frequencies in Hz. Each frequency is
expressed as a 64-bit big-endian integer. The frequency
is half of the bps per lane due to DDR transmission.
For further information on the MIPI CSI-2 endpoint node properties, see
Documentation/devicetree/bindings/media/video-interfaces.txt.
Example:
tc358743@0f {
compatible = "toshiba,tc358743";
reg = <0x0f>;
clocks = <&hdmi_osc>;
clock-names = "refclk";
reset-gpios = <&gpio6 9 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpio2>;
interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
port {
tc358743_out: endpoint {
remote-endpoint = <&mipi_csi2_in>;
data-lanes = <1 2 3 4>;
clock-lanes = <0>;
clock-noncontinuous;
link-frequencies = /bits/ 64 <297000000>;
};
};
};
......@@ -29,7 +29,9 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/videodev2.h>
#include <linux/workqueue.h>
#include <linux/v4l2-dv-timings.h>
......@@ -37,6 +39,7 @@
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-of.h>
#include <media/tc358743.h>
#include "tc358743_regs.h"
......@@ -69,6 +72,7 @@ static const struct v4l2_dv_timings_cap tc358743_timings_cap = {
struct tc358743_state {
struct tc358743_platform_data pdata;
struct v4l2_of_bus_mipi_csi2 bus;
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler hdl;
......@@ -90,6 +94,8 @@ struct tc358743_state {
struct v4l2_dv_timings timings;
u32 mbus_fmt_code;
struct gpio_desc *reset_gpio;
};
static void tc358743_enable_interrupts(struct v4l2_subdev *sd,
......@@ -700,7 +706,8 @@ static void tc358743_set_csi(struct v4l2_subdev *sd)
((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) |
((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0));
i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE);
i2c_wr32(sd, TXOPTIONCNTRL, (state->bus.flags &
V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ? MASK_CONTCLKMODE : 0);
i2c_wr32(sd, STARTCNTRL, MASK_START);
i2c_wr32(sd, CSI_START, MASK_STRT);
......@@ -1638,6 +1645,136 @@ static const struct v4l2_ctrl_config tc358743_ctrl_audio_present = {
/* --------------- PROBE / REMOVE --------------- */
#ifdef CONFIG_OF
static void tc358743_gpio_reset(struct tc358743_state *state)
{
gpiod_set_value(state->reset_gpio, 0);
usleep_range(5000, 10000);
gpiod_set_value(state->reset_gpio, 1);
usleep_range(1000, 2000);
gpiod_set_value(state->reset_gpio, 0);
msleep(20);
}
static int tc358743_probe_of(struct tc358743_state *state)
{
struct device *dev = &state->i2c_client->dev;
struct v4l2_of_endpoint *endpoint;
struct device_node *ep;
struct clk *refclk;
u32 bps_pr_lane;
int ret = -EINVAL;
refclk = devm_clk_get(dev, "refclk");
if (IS_ERR(refclk)) {
if (PTR_ERR(refclk) != -EPROBE_DEFER)
dev_err(dev, "failed to get refclk: %ld\n",
PTR_ERR(refclk));
return PTR_ERR(refclk);
}
ep = of_graph_get_next_endpoint(dev->of_node, NULL);
if (!ep) {
dev_err(dev, "missing endpoint node\n");
return -EINVAL;
}
endpoint = v4l2_of_alloc_parse_endpoint(ep);
if (IS_ERR(endpoint)) {
dev_err(dev, "failed to parse endpoint\n");
return PTR_ERR(endpoint);
}
if (endpoint->bus_type != V4L2_MBUS_CSI2 ||
endpoint->bus.mipi_csi2.num_data_lanes == 0 ||
endpoint->nr_of_link_frequencies == 0) {
dev_err(dev, "missing CSI-2 properties in endpoint\n");
goto free_endpoint;
}
state->bus = endpoint->bus.mipi_csi2;
clk_prepare_enable(refclk);
state->pdata.refclk_hz = clk_get_rate(refclk);
state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS;
state->pdata.enable_hdcp = false;
/* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */
state->pdata.fifo_level = 16;
/*
* The PLL input clock is obtained by dividing refclk by pll_prd.
* It must be between 6 MHz and 40 MHz, lower frequency is better.
*/
switch (state->pdata.refclk_hz) {
case 26000000:
case 27000000:
case 42000000:
state->pdata.pll_prd = state->pdata.refclk_hz / 6000000;
break;
default:
dev_err(dev, "unsupported refclk rate: %u Hz\n",
state->pdata.refclk_hz);
goto disable_clk;
}
/*
* The CSI bps per lane must be between 62.5 Mbps and 1 Gbps.
* The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60.
*/
bps_pr_lane = 2 * endpoint->link_frequencies[0];
if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) {
dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane);
goto disable_clk;
}
/* The CSI speed per lane is refclk / pll_prd * pll_fbd */
state->pdata.pll_fbd = bps_pr_lane /
state->pdata.refclk_hz * state->pdata.pll_prd;
/*
* FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz
* link frequency). In principle it should be possible to calculate
* them based on link frequency and resolution.
*/
if (bps_pr_lane != 594000000U)
dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane);
state->pdata.lineinitcnt = 0xe80;
state->pdata.lptxtimecnt = 0x003;
/* tclk-preparecnt: 3, tclk-zerocnt: 20 */
state->pdata.tclk_headercnt = 0x1403;
state->pdata.tclk_trailcnt = 0x00;
/* ths-preparecnt: 3, ths-zerocnt: 1 */
state->pdata.ths_headercnt = 0x0103;
state->pdata.twakeup = 0x4882;
state->pdata.tclk_postcnt = 0x008;
state->pdata.ths_trailcnt = 0x2;
state->pdata.hstxvregcnt = 0;
state->reset_gpio = devm_gpiod_get(dev, "reset");
if (IS_ERR(state->reset_gpio)) {
dev_err(dev, "failed to get reset gpio\n");
ret = PTR_ERR(state->reset_gpio);
goto disable_clk;
}
tc358743_gpio_reset(state);
ret = 0;
goto free_endpoint;
disable_clk:
clk_disable_unprepare(refclk);
free_endpoint:
v4l2_of_free_endpoint(endpoint);
return ret;
}
#else
static inline int tc358743_probe_of(struct tc358743_state *state)
{
return -ENODEV;
}
#endif
static int tc358743_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
......@@ -1658,14 +1795,20 @@ static int tc358743_probe(struct i2c_client *client,
if (!state)
return -ENOMEM;
state->i2c_client = client;
/* platform data */
if (!pdata) {
v4l_err(client, "No platform data!\n");
return -ENODEV;
if (pdata) {
state->pdata = *pdata;
state->bus.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
} else {
err = tc358743_probe_of(state);
if (err == -ENODEV)
v4l_err(client, "No platform data!\n");
if (err)
return err;
}
state->pdata = *pdata;
state->i2c_client = client;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &tc358743_ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
......
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