lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <56C1C3C5.1080004@xs4all.nl>
Date:	Mon, 15 Feb 2016 13:25:41 +0100
From:	Hans Verkuil <hverkuil@...all.nl>
To:	Jean-Michel Hautbois <jhautbois@...il.com>,
	linux-media@...r.kernel.org, linux-kernel@...r.kernel.org,
	devicetree@...r.kernel.org
Cc:	jslaby@...e.com, joe@...ches.com, kvalo@...eaurora.org,
	davem@...emloft.net, gregkh@...uxfoundation.org,
	akpm@...ux-foundation.org, mchehab@....samsung.com,
	galak@...eaurora.org, ijc+devicetree@...lion.org.uk,
	mark.rutland@....com, pawel.moll@....com, robh+dt@...nel.org,
	laurent.pinchart@...asonboard.com,
	Jean-Michel Hautbois <jean-michel.hautbois@...-labs.com>
Subject: Re: [PATCH v6] media: spi: Add support for LMH0395

Hi Jean-Michel,

Some minor comments below.


On 02/08/2016 12:20 PM, Jean-Michel Hautbois wrote:
> This device is a SPI based device from TI.
> It is a 3 Gbps HD/SD SDI Dual Output Low Power
> Extended Reach Adaptive Cable Equalizer.
> 
> LMH0395 enables the use of up to two outputs.
> These can be configured using DT.
> The name gives the spi bus, and the CS associated.
> Example : lmh0395-1@...2
> LMH0395 is on bus SPI2 with CS number 1.
> 
> Controls should be accessible from userspace too.
> This will have to be done later.
> 
> Signed-off-by: Jean-Michel Hautbois <jean-michel.hautbois@...-labs.com>
> ---
> v2: Add DT support
> v3: Change the bit set/clear in output_type as disabled means 'set the bit'
> v4: Clearer description of endpoints usage in Doc, and some init changes.
>     Add a dependency on OF and don't test CONFIG_OF anymore.
> v5: Change port description in Documentation
>     Multiple ports : required #address-cells and #size-cells
>     Alphabetical order for include files
>     Simplify register set/clear
>     Check device ID
>     Implement log_status handler
> v6: Take Laurent Pinchart remarks into account
>     Correct register settings
>     Use next generation MC
> 
>  .../devicetree/bindings/media/spi/lmh0395.txt      |  51 +++
>  MAINTAINERS                                        |   6 +
>  drivers/media/Kconfig                              |   1 +
>  drivers/media/Makefile                             |   2 +-
>  drivers/media/spi/Kconfig                          |  15 +
>  drivers/media/spi/Makefile                         |   1 +
>  drivers/media/spi/lmh0395.c                        | 477 +++++++++++++++++++++
>  drivers/media/spi/lmh039x.h                        |  55 +++
>  8 files changed, 607 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/devicetree/bindings/media/spi/lmh0395.txt
>  create mode 100644 drivers/media/spi/Kconfig
>  create mode 100644 drivers/media/spi/Makefile
>  create mode 100644 drivers/media/spi/lmh0395.c
>  create mode 100644 drivers/media/spi/lmh039x.h
> 
> diff --git a/Documentation/devicetree/bindings/media/spi/lmh0395.txt b/Documentation/devicetree/bindings/media/spi/lmh0395.txt
> new file mode 100644
> index 0000000..5c6ab4a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/spi/lmh0395.txt
> @@ -0,0 +1,51 @@
> +* Texas Instruments lmh0395 3G HD/SD SDI equalizer
> +
> +"The LMH0395 is an SDI equalizer designed to extend the reach of SDI signals
> +transmitted over cable by equalizing the input signal and generating clean
> +outputs. It has one differential input and two differential output that can be
> +independently controlled."
> +
> +Required Properties :
> +- compatible: Must be "ti,lmh0395"
> +
> +The device node must contain one 'port' child node per device input and output
> +port, in accordance with the video interface bindings defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +The LMH0395 has three ports numbered as follows.
> +
> +  Port			LMH0395
> +------------------------------------------------------------
> +  SDI (SDI input)	0
> +  SDO0 (SDI output 0)	1
> +  SDO1 (SDI output 1)	2
> +
> +Example:
> +
> +ecspi@...10000 {
> +	...
> +	...
> +
> +	lmh0395@1 {
> +		compatible = "ti,lmh0395";
> +		reg = <1>;
> +		spi-max-frequency = <20000000>;
> +		ports {
> +			port@0 {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +				reg = <0>;
> +				sdi0_in: endpoint {};
> +			};
> +			port@1 {
> +				reg = <1>;
> +				sdi0_out0: endpoint {};
> +			};
> +			port@2 {
> +				reg = <2>;
> +				/* endpoint not specified, disable output */
> +			};
> +		};
> +	};
> +	...
> +};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 30aca4a..d155df5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10889,6 +10889,12 @@ S:	Maintained
>  F:	sound/soc/codecs/lm49453*
>  F:	sound/soc/codecs/isabelle*
>  
> +TI LMH0395 DRIVER
> +M:	Jean-Michel Hautbois <jean-michel.hautbois@...-labs.com>
> +L:	linux-media@...r.kernel.org
> +S:	Maintained
> +F:	drivers/media/spi/lmh039*
> +
>  TI LP855x BACKLIGHT DRIVER
>  M:	Milo Kim <milo.kim@...com>
>  S:	Maintained
> diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
> index a8518fb..4b38792 100644
> --- a/drivers/media/Kconfig
> +++ b/drivers/media/Kconfig
> @@ -213,6 +213,7 @@ config MEDIA_ATTACH
>  	default MODULES
>  
>  source "drivers/media/i2c/Kconfig"
> +source "drivers/media/spi/Kconfig"
>  source "drivers/media/tuners/Kconfig"
>  source "drivers/media/dvb-frontends/Kconfig"
>  
> diff --git a/drivers/media/Makefile b/drivers/media/Makefile
> index e608bbc..6a64e96 100644
> --- a/drivers/media/Makefile
> +++ b/drivers/media/Makefile
> @@ -8,7 +8,7 @@ media-objs	:= media-device.o media-devnode.o media-entity.o
>  # I2C drivers should come before other drivers, otherwise they'll fail
>  # when compiled as builtin drivers
>  #
> -obj-y += i2c/ tuners/
> +obj-y += i2c/ tuners/ spi/
>  obj-$(CONFIG_DVB_CORE)  += dvb-frontends/
>  
>  #
> diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig
> new file mode 100644
> index 0000000..a9d06cb
> --- /dev/null
> +++ b/drivers/media/spi/Kconfig
> @@ -0,0 +1,15 @@
> +if VIDEO_V4L2
> +
> +config VIDEO_LMH0395
> +	tristate "LMH0395 equalizer"
> +	depends on VIDEO_V4L2 && SPI && VIDEO_V4L2_SUBDEV_API
> +	depends on MEDIA_CONTROLLER && OF
> +	---help---
> +	  Support for TI LMH0395 3G HD/SD SDI Dual Output Low Power
> +	  Extended Reach Adaptive Cable Equalizer.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called lmh0395.
> +
> +
> +endif
> diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile
> new file mode 100644
> index 0000000..6c587e5
> --- /dev/null
> +++ b/drivers/media/spi/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_VIDEO_LMH0395)	+= lmh0395.o
> diff --git a/drivers/media/spi/lmh0395.c b/drivers/media/spi/lmh0395.c
> new file mode 100644
> index 0000000..bf6b52c
> --- /dev/null
> +++ b/drivers/media/spi/lmh0395.c
> @@ -0,0 +1,477 @@
> +/*
> + * LMH0395 SPI driver.
> + * Copyright (C) 2016  Jean-Michel Hautbois
> + *
> + * 3G HD/SD SDI Dual Output Low Power Extended Reach Adaptive Cable Equalizer
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/ioctl.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/uaccess.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-of.h>
> +#include "lmh039x.h"
> +
> +static const char * const lmh039x_output_strs[] = {
> +	"No Output Driver",
> +	"Output Driver 0",
> +	"Output Driver 1",
> +	"Output Driver 0+1",
> +};
> +
> +/* spi implementation */
> +
> +static int lmh0395_spi_write(struct spi_device *spi, u8 reg, u8 data)
> +{
> +	int err;
> +	u8 cmd[2];
> +
> +	cmd[0] = LMH0395_SPI_CMD_WRITE | reg;
> +	cmd[1] = data;
> +
> +	err = spi_write(spi, cmd, 2);
> +	if (err < 0) {
> +		dev_err(&spi->dev, "SPI write failed : %d\n", err);
> +		return err;
> +	}
> +
> +	return err;
> +}
> +
> +static int lmh0395_spi_read(struct spi_device *spi, u8 reg, unsigned long *data)
> +{
> +	int err;
> +	u8 cmd[2];
> +	u8 read_data[2];
> +
> +	cmd[0] = LMH0395_SPI_CMD_READ | reg;
> +	cmd[1] = 0xff;
> +
> +	err = spi_write(spi, cmd, 2);
> +	if (err < 0) {
> +		dev_err(&spi->dev, "SPI failed to select reg : %d\n", err);
> +		return err;
> +	}
> +
> +	err = spi_read(spi, read_data, 2);
> +	if (err < 0) {
> +		dev_err(&spi->dev, "SPI failed to read reg : %d\n", err);
> +		return err;
> +	}
> +	/* The first 8 bits is the address used, drop it */
> +	*data = read_data[1];
> +
> +	return err;
> +}
> +
> +struct lmh0395_state {
> +	struct v4l2_subdev sd;
> +	struct media_pad pads[LMH0395_PADS_NUM];
> +	enum lmh0395_output_type output_type;
> +};
> +
> +static inline struct lmh0395_state *to_state(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct lmh0395_state, sd);
> +}
> +
> +static bool lmh0395_carrier_detect(struct v4l2_subdev *sd)
> +{
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +	unsigned long reg;
> +
> +	lmh0395_spi_read(spi, LMH0395_GENERAL_CTRL, &reg);
> +
> +	if (reg & 0x80)
> +		return true;
> +	else
> +		return false;

Just do:

	return (reg & 0x80) ? true : false;

> +}
> +
> +static int lmh0395_get_rate(struct v4l2_subdev *sd, u8 *rate)
> +{
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +	int err;
> +	unsigned long ctrl;
> +
> +	if (!lmh0395_carrier_detect(sd))
> +		return 0;
> +
> +	err = lmh0395_spi_read(spi, LMH0395_RATE_INDICATOR, &ctrl);
> +	if (err < 0)
> +		return err;
> +
> +	*rate = ctrl & 0x20;
> +	dev_dbg(&spi->dev, "Rate : %s\n", (ctrl & 0x20) ? "3G/HD" : "SD");
> +	return 0;
> +}
> +
> +static int lmh0395_get_launch_amp(struct v4l2_subdev *sd)
> +{
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +	unsigned long reg;
> +	int launch_amp;
> +	int err;
> +
> +	err = lmh0395_spi_read(spi, LMH0395_LAUNCH_AMP_INDICATION, &reg);
> +	if (err < 0)
> +		return err;
> +
> +	launch_amp = ((reg & 0xfc) >> 2);

No need for the outer parenthesis.

> +	launch_amp = launch_amp - 32;
> +
> +	dev_dbg(&spi->dev, "Launch amplitude : %d\n", launch_amp);
> +
> +	return launch_amp;
> +}
> +
> +static int lmh0395_get_cable_length(struct v4l2_subdev *sd, u8 rate)
> +{
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +	u8 length;
> +	unsigned long cli;
> +	int err;
> +
> +	err = lmh0395_spi_read(spi, LMH0395_CABLE_LENGTH_INDICATOR, &cli);
> +	if (err < 0)
> +		return err;
> +
> +	/* The cable length indicator (CLI) provides an indication of the
> +	 * length of the cable attached to input. CLI is accessible via bits
> +	 * [7:0] of SPI register 06h.
> +	 * The 8-bit setting ranges in decimal value from 0 to 247
> +	 * ("00000000" to "11110111" binary), corresponding to 0 to 400m of
> +	 * Belden 1694A cable.
> +	 * For 3G and HD input, CLI is 1.25m per step.
> +	 * For SD input, CLI is 1.25m per step, less 20m, from 0 to 191 decimal
> +	 * and 3.5m per step from 192 to 247 decimal.
> +	 */
> +
> +	length = cli*5/4;
> +	if (rate == 0) {
> +		if (cli <= 191)
> +			length -= 20;
> +		else
> +			length = ((191*5/4)-20) + ((cli-191)*7/2);
> +
> +	}
> +	dev_dbg(&spi->dev, "Length estimated (BELDEN 1694A cables) : %dm\n",
> +			length);
> +	return length;
> +}
> +
> +static int lmh0395_set_output_type(struct v4l2_subdev *sd, unsigned long output)
> +{
> +	struct lmh0395_state *state = to_state(sd);
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +	unsigned long muteref_reg;
> +
> +	/* Get the current register status */
> +	lmh0395_spi_read(spi, LMH0395_MUTE_REF, &muteref_reg);
> +	switch (output) {
> +	case LMH0395_OUTPUT_TYPE_SDO0:
> +		clear_bit(6, &muteref_reg);
> +		break;
> +	case LMH0395_OUTPUT_TYPE_SDO1:
> +		clear_bit(7, &muteref_reg);
> +		break;
> +	case LMH0395_OUTPUT_TYPE_BOTH:
> +		clear_bit(6, &muteref_reg);
> +		clear_bit(7, &muteref_reg);
> +		break;
> +	case LMH0395_OUTPUT_TYPE_NONE:
> +		set_bit(6, &muteref_reg);
> +		set_bit(7, &muteref_reg);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	state->output_type = output;
> +	return 0;
> +
> +}
> +
> +static int lmh0395_get_control(struct v4l2_subdev *sd)
> +{
> +	int err;
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +	unsigned long ctrl;
> +	u8 rate = 0;
> +
> +	err = lmh0395_spi_read(spi, LMH0395_GENERAL_CTRL, &ctrl);
> +	if (err < 0)
> +		return err;
> +
> +	if (ctrl & 0x80) {
> +		dev_dbg(&spi->dev, "Carrier detected\n");
> +		lmh0395_get_rate(sd, &rate);
> +		lmh0395_get_cable_length(sd, rate);
> +		lmh0395_get_launch_amp(sd);
> +	}
> +	return 0;
> +}
> +
> +static int lmh0395_get_output_status(struct v4l2_subdev *sd)
> +{
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +	unsigned long muteref_reg;
> +
> +	/* Get the current register status */
> +	lmh0395_spi_read(spi, LMH0395_MUTE_REF, &muteref_reg);
> +	dev_dbg(&spi->dev, "Output 0 is %s\n",
> +			test_bit(7, &muteref_reg) ? "enabled" : "disabled");
> +	dev_dbg(&spi->dev, "Output 1 is %s\n",
> +			test_bit(6, &muteref_reg) ? "enabled" : "disabled");
> +	return 0;
> +}
> +
> +static int lmh0395_log_status(struct v4l2_subdev *sd)
> +{
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +
> +	dev_dbg(&spi->dev, "-----Chip status-----\n");
> +	lmh0395_get_output_status(sd);
> +	lmh0395_get_control(sd);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int lmh0395_g_register(struct v4l2_subdev *sd,
> +					struct v4l2_dbg_register *reg)
> +{
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +	int err = 0;
> +	unsigned long val;
> +
> +	reg->size = 1;
> +	reg->val = 0;
> +
> +	/* Don't try to access over last register */
> +	if (reg->reg > LMH0395_LAUNCH_AMP_INDICATION)
> +		return 0;
> +
> +	err = lmh0395_spi_read(spi, reg->reg, &val);
> +	if (!err)
> +		reg->val = val;
> +
> +	return err;
> +}
> +static int lmh0395_s_register(struct v4l2_subdev *sd,
> +					const struct v4l2_dbg_register *reg)
> +{
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +	int err = 0;
> +
> +	/* Don't try to access over last register */
> +	if (reg->reg > LMH0395_LAUNCH_AMP_INDICATION)
> +		return -EINVAL;
> +
> +	err = lmh0395_spi_write(spi, reg->reg, reg->val);

Just do return lmh0395_spi_write and you can drop the 'err' variable.

> +
> +	return err;
> +}
> +#endif
> +
> +static int lmh0395_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
> +				u32 config)
> +{
> +	struct lmh0395_state *state = to_state(sd);
> +
> +	if (state->output_type == output)
> +		return 0;
> +
> +	return lmh0395_set_output_type(sd, output);
> +}
> +
> +static int lmh0395_registered(struct v4l2_subdev *sd)
> +{
> +	struct spi_device *spi = v4l2_get_subdevdata(sd);
> +
> +	dev_dbg(&spi->dev, "subdev registered\n");
> +	lmh0395_set_output_type(sd, LMH0395_OUTPUT_TYPE_BOTH);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_internal_ops lmh0395_internal_ops = {
> +	.registered = lmh0395_registered,
> +};
> +
> +static const struct v4l2_subdev_video_ops lmh0395_video_ops = {
> +	.s_routing = lmh0395_s_routing,
> +};
> +
> +static const struct v4l2_subdev_core_ops lmh0395_core_ops = {
> +	.log_status = lmh0395_log_status,
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.g_register = lmh0395_g_register,
> +	.s_register = lmh0395_s_register,
> +#endif
> +};
> +
> +static const struct v4l2_subdev_ops lmh0395_ops = {
> +	.core = &lmh0395_core_ops,
> +	.video = &lmh0395_video_ops,
> +};
> +
> +struct lmh0395_dev {
> +	unsigned long dev_id;
> +	char *name;
> +};
> +
> +static const struct lmh0395_dev lmh0395_dev[] = {
> +	{
> +		.dev_id = ID_LMH0384,
> +		.name = "LMH0384",
> +	},
> +	{
> +		.dev_id = ID_LMH0394,
> +		.name = "LMH0394",
> +	},
> +	{
> +		.dev_id = ID_LMH0395,
> +		.name = "LMH0395",
> +	},
> +	{ /* sentinel */ },
> +};
> +
> +static const struct spi_device_id lmh0395_id[] = {
> +	{ "lmh0395", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(spi, lmh0395_id);
> +
> +static const struct of_device_id lmh0395_of_match[] = {
> +	{.compatible = "ti,lmh0395", },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, lmh0395_of_match);
> +
> +static int lmh0395_probe(struct spi_device *spi)
> +{
> +	unsigned long device_id;
> +	struct lmh0395_state *state;
> +	struct v4l2_subdev *sd;
> +	int err, i;
> +	struct device_node *n = NULL;
> +	struct of_endpoint ep;
> +
> +	err = lmh0395_spi_read(spi, LMH0395_DEVICE_ID, &device_id);
> +	if (err < 0)
> +		return err;
> +
> +	for (i = 0 ; i < ARRAY_SIZE(lmh0395_dev) ; i++) {

No space before ';'.

> +		if (device_id == lmh0395_dev[i].dev_id)
> +			break;
> +	}
> +	if (i == ARRAY_SIZE(lmh0395_dev)) {
> +		dev_err(&spi->dev, "Device not supported (id = %08lx)\n",
> +					device_id);
> +		return -ENODEV;
> +	}
> +	dev_dbg(&spi->dev, "%s detected\n", lmh0395_dev[i].name);
> +
> +	/* Now that the device is here, let's init V4L2 */
> +	state = devm_kzalloc(&spi->dev, sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return -ENOMEM;
> +
> +	sd = &state->sd;
> +
> +	if (spi->dev.of_node) {
> +		dev_dbg(&spi->dev, "Parsing DT configuration\n");
> +		while ((n = of_graph_get_next_endpoint(spi->dev.of_node, n))
> +								!= NULL) {
> +			err = of_graph_parse_endpoint(n, &ep);
> +			if (err < 0) {
> +				dev_err(&spi->dev, "Could not parse endpoint: %d\n", err);
> +				of_node_put(n);
> +				return err;
> +			}
> +			dev_dbg(&spi->dev, "endpoint %d on port %d\n",
> +						ep.id, ep.port);
> +			of_node_put(n);
> +		}
> +	} else {
> +		dev_dbg(&spi->dev, "No DT configuration\n");
> +	}
> +
> +	v4l2_spi_subdev_init(sd, spi, &lmh0395_ops);
> +	sd->internal_ops = &lmh0395_internal_ops;
> +
> +	snprintf(sd->name, sizeof(sd->name), "%s-%d@...%d",
> +		spi->dev.driver->name,
> +		spi->chip_select,
> +		spi->master->bus_num);
> +	dev_dbg(&spi->dev, "%s named %s\n", lmh0395_dev[i].name, sd->name);
> +
> +	state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	state->pads[LMH0395_SDI_INPUT].flags = MEDIA_PAD_FL_SINK;
> +	state->pads[LMH0395_SDI_OUT0].flags = MEDIA_PAD_FL_SOURCE;
> +	state->pads[LMH0395_SDI_OUT1].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	err = media_entity_pads_init(&sd->entity,
> +				     LMH0395_PADS_NUM, state->pads);
> +	if (err) {
> +		dev_err(&spi->dev, "entity init failed\n");
> +		spi_unregister_device(spi);
> +		return err;
> +	}
> +
> +	dev_dbg(&spi->dev, "Entity initialized\n");
> +
> +	err = v4l2_async_register_subdev(sd);
> +	if (err < 0) {
> +		media_entity_cleanup(&sd->entity);
> +		spi_unregister_device(spi);
> +		return err;
> +	}
> +
> +	dev_dbg(&spi->dev, "device probed\n");
> +
> +	return 0;
> +}
> +
> +static int lmh0395_remove(struct spi_device *spi)
> +{
> +	struct v4l2_subdev *sd = spi_get_drvdata(spi);
> +
> +	v4l2_async_unregister_subdev(sd);
> +	media_entity_cleanup(&sd->entity);
> +	spi_unregister_device(spi);
> +	return 0;
> +}
> +
> +static struct spi_driver lmh0395_driver = {
> +	.driver = {
> +		.of_match_table = lmh0395_of_match,
> +		.name = "lmh0395",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = lmh0395_probe,
> +	.remove = lmh0395_remove,
> +	.id_table = lmh0395_id,
> +};
> +
> +module_spi_driver(lmh0395_driver);
> +
> +MODULE_DESCRIPTION("spi device driver for LMH0395 equalizer");
> +MODULE_AUTHOR("Jean-Michel Hautbois");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/spi/lmh039x.h b/drivers/media/spi/lmh039x.h
> new file mode 100644
> index 0000000..f3e35e4
> --- /dev/null
> +++ b/drivers/media/spi/lmh039x.h
> @@ -0,0 +1,55 @@
> +/*
> + * LMH0395 SPI driver.
> + * Copyright (C) 2014  Jean-Michel Hautbois
> + *
> + * 3G HD/SD SDI Dual Output Low Power Extended Reach Adaptive Cable Equalizer
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _LMH039X_
> +#define _LMH039X_
> +
> +#include <media/v4l2-device.h>
> +
> +#define	LMH0395_SPI_CMD_WRITE	0x00
> +#define	LMH0395_SPI_CMD_READ	0x80
> +
> +/* Registers of LMH0395 */
> +#define LMH0395_GENERAL_CTRL		0x00
> +#define LMH0395_OUTPUT_DRIVER		0x01
> +#define LMH0395_LAUNCH_AMP_CTRL		0x02
> +#define LMH0395_MUTE_REF		0x03
> +#define LMH0395_DEVICE_ID		0x04
> +#define	LMH0395_RATE_INDICATOR		0x05
> +#define	LMH0395_CABLE_LENGTH_INDICATOR	0x06
> +#define	LMH0395_LAUNCH_AMP_INDICATION	0x07
> +
> +/* This is a one input, dual output device */
> +#define LMH0395_SDI_INPUT	0
> +#define LMH0395_SDI_OUT0	1
> +#define LMH0395_SDI_OUT1	2
> +
> +#define LMH0395_PADS_NUM	3
> +
> +#define ID_LMH0384		0x03
> +#define ID_LMH0394		0x13
> +#define ID_LMH0395		0x23
> +
> +/* Register LMH0395_MUTE_REF bits [7:6] */
> +enum lmh0395_output_type {
> +	LMH0395_OUTPUT_TYPE_NONE,
> +	LMH0395_OUTPUT_TYPE_SDO0,
> +	LMH0395_OUTPUT_TYPE_SDO1,
> +	LMH0395_OUTPUT_TYPE_BOTH
> +};
> +
> +#endif
> 

Regards,

	Hans

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ