Skip to content

Commit

Permalink
[media] tc358743: support probe from device tree
Browse files Browse the repository at this point in the history
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: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
  • Loading branch information
Philipp Zabel authored and Mauro Carvalho Chehab committed Jul 22, 2015
1 parent 8ec23da commit 2561482
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 6 deletions.
48 changes: 48 additions & 0 deletions Documentation/devicetree/bindings/media/i2c/tc358743.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
* 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>;
};
};
};
155 changes: 149 additions & 6 deletions drivers/media/i2c/tc358743.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@
#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>
#include <linux/hdmi.h>
#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"
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand Down

0 comments on commit 2561482

Please sign in to comment.