linux/drivers/media/i2c/tda1997x.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2018 Gateworks Corporation
 */
#include <linux/delay.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
#include <linux/v4l2-dv-timings.h>
#include <linux/videodev2.h>

#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/i2c/tda1997x.h>

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>

#include <dt-bindings/media/tda1997x.h>

#include "tda1997x_regs.h"

#define TDA1997X_MBUS_CODES

/* debug level */
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC();

/* Audio formats */
static const char * const audtype_names[] =;

/* Audio output port formats */
enum audfmt_types {};
static const char * const audfmt_names[] =;

/* Video input formats */
static const char * const hdmi_colorspace_names[] =;
static const char * const hdmi_colorimetry_names[] =;
static const char * const v4l2_quantization_names[] =;

/* Video output port formats */
static const char * const vidfmt_names[] =;

/*
 * Colorspace conversion matrices
 */
struct color_matrix_coefs {};

enum {};

/* NB: 4096 is 1.0 using fixed point numbers */
static const struct color_matrix_coefs conv_matrix[] =;

static const struct v4l2_dv_timings_cap tda1997x_dv_timings_cap =;

/* regulator supplies */
static const char * const tda1997x_supply_name[] =;

#define TDA1997X_NUM_SUPPLIES

enum tda1997x_type {};

enum tda1997x_hdmi_pads {};

struct tda1997x_chip_info {};

struct tda1997x_state {};

static const struct v4l2_event tda1997x_ev_fmt =;

static const struct tda1997x_chip_info tda1997x_chip_info[] =;

static inline struct tda1997x_state *to_state(struct v4l2_subdev *sd)
{}

static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{}

static int tda1997x_cec_read(struct v4l2_subdev *sd, u8 reg)
{}

static int tda1997x_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{}

/* -----------------------------------------------------------------------------
 * I2C transfer
 */

static int tda1997x_setpage(struct v4l2_subdev *sd, u8 page)
{}

static inline int io_read(struct v4l2_subdev *sd, u16 reg)
{}

static inline long io_read16(struct v4l2_subdev *sd, u16 reg)
{}

static inline long io_read24(struct v4l2_subdev *sd, u16 reg)
{}

static unsigned int io_readn(struct v4l2_subdev *sd, u16 reg, u8 len, u8 *data)
{}

static int io_write(struct v4l2_subdev *sd, u16 reg, u8 val)
{}

static int io_write16(struct v4l2_subdev *sd, u16 reg, u16 val)
{}

static int io_write24(struct v4l2_subdev *sd, u16 reg, u32 val)
{}

/* -----------------------------------------------------------------------------
 * Hotplug
 */

enum hpd_mode {};

/* manual HPD (Hot Plug Detect) control */
static int tda1997x_manual_hpd(struct v4l2_subdev *sd, enum hpd_mode mode)
{}

static void tda1997x_delayed_work_enable_hpd(struct work_struct *work)
{}

static void tda1997x_disable_edid(struct v4l2_subdev *sd)
{}

static void tda1997x_enable_edid(struct v4l2_subdev *sd)
{}

/* -----------------------------------------------------------------------------
 * Signal Control
 */

/*
 * configure vid_fmt based on mbus_code
 */
static int
tda1997x_setup_format(struct tda1997x_state *state, u32 code)
{}

/*
 * The color conversion matrix will convert between the colorimetry of the
 * HDMI input to the desired output format RGB|YUV. RGB output is to be
 * full-range and YUV is to be limited range.
 *
 * RGB full-range uses values from 0 to 255 which is recommended on a monitor
 * and RGB Limited uses values from 16 to 236 (16=black, 235=white) which is
 * typically recommended on a TV.
 */
static void
tda1997x_configure_csc(struct v4l2_subdev *sd)
{}

/* Configure frame detection window and VHREF timing generator */
static void
tda1997x_configure_vhref(struct v4l2_subdev *sd)
{}

/* Configure Video Output port signals */
static int
tda1997x_configure_vidout(struct tda1997x_state *state)
{}

/* Configure Audio output port signals */
static int
tda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment)
{}

/* Soft Reset of specific hdmi info */
static int
tda1997x_hdmi_info_reset(struct v4l2_subdev *sd, u8 info_rst, bool reset_sus)
{}

static void
tda1997x_power_mode(struct tda1997x_state *state, bool enable)
{}

static bool
tda1997x_detect_tx_5v(struct v4l2_subdev *sd)
{}

static bool
tda1997x_detect_tx_hpd(struct v4l2_subdev *sd)
{}

static int
tda1997x_detect_std(struct tda1997x_state *state,
		    struct v4l2_dv_timings *timings)
{}

/* some sort of errata workaround for chip revision 0 (N1) */
static void tda1997x_reset_n1(struct tda1997x_state *state)
{}

/*
 * Activity detection must only be notified when stable_clk_x AND active_x
 * bits are set to 1. If only stable_clk_x bit is set to 1 but not
 * active_x, it means that the TMDS clock is not in the defined range
 * and activity detection must not be notified.
 */
static u8
tda1997x_read_activity_status_regs(struct v4l2_subdev *sd)
{}

static void
set_rgb_quantization_range(struct tda1997x_state *state)
{}

/* parse an infoframe and do some sanity checks on it */
static unsigned int
tda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr)
{}

static void tda1997x_irq_sus(struct tda1997x_state *state, u8 *flags)
{}

static void tda1997x_irq_ddc(struct tda1997x_state *state, u8 *flags)
{}

static void tda1997x_irq_rate(struct tda1997x_state *state, u8 *flags)
{}

static void tda1997x_irq_info(struct tda1997x_state *state, u8 *flags)
{}

static void tda1997x_irq_audio(struct tda1997x_state *state, u8 *flags)
{}

static void tda1997x_irq_hdcp(struct tda1997x_state *state, u8 *flags)
{}

static irqreturn_t tda1997x_isr_thread(int irq, void *d)
{}

/* -----------------------------------------------------------------------------
 * v4l2_subdev_video_ops
 */

static int
tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status)
{
	struct tda1997x_state *state = to_state(sd);
	u32 vper;
	u16 hper;
	u16 hsper;

	mutex_lock(&state->lock);
	vper = io_read24(sd, REG_V_PER) & MASK_VPER;
	hper = io_read16(sd, REG_H_PER) & MASK_HPER;
	hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH;
	/*
	 * The tda1997x supports A/B inputs but only a single output.
	 * The irq handler monitors for timing changes on both inputs and
	 * sets the input_detect array to 0|1 depending on signal presence.
	 * I believe selection of A vs B is automatic.
	 *
	 * The vper/hper/hsper registers provide the frame period, line period
	 * and horiz sync period (units of MCLK clock cycles (27MHz)) and
	 * testing shows these values to be random if no signal is present
	 * or locked.
	 */
	v4l2_dbg(1, debug, sd, "inputs:%d/%d timings:%d/%d/%d\n",
		 state->input_detect[0], state->input_detect[1],
		 vper, hper, hsper);
	if (!state->input_detect[0] && !state->input_detect[1])
		*status = V4L2_IN_ST_NO_SIGNAL;
	else if (!vper || !hper || !hsper)
		*status = V4L2_IN_ST_NO_SYNC;
	else
		*status = 0;
	mutex_unlock(&state->lock);

	return 0;
};

static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
				 struct v4l2_dv_timings *timings)
{}

static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
				 struct v4l2_dv_timings *timings)
{}

static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
				     struct v4l2_dv_timings *timings)
{}

static const struct v4l2_subdev_video_ops tda1997x_video_ops =;


/* -----------------------------------------------------------------------------
 * v4l2_subdev_pad_ops
 */

static int tda1997x_init_state(struct v4l2_subdev *sd,
			       struct v4l2_subdev_state *sd_state)
{}

static int tda1997x_enum_mbus_code(struct v4l2_subdev *sd,
				  struct v4l2_subdev_state *sd_state,
				  struct v4l2_subdev_mbus_code_enum *code)
{}

static void tda1997x_fill_format(struct tda1997x_state *state,
				 struct v4l2_mbus_framefmt *format)
{}

static int tda1997x_get_format(struct v4l2_subdev *sd,
			       struct v4l2_subdev_state *sd_state,
			       struct v4l2_subdev_format *format)
{}

static int tda1997x_set_format(struct v4l2_subdev *sd,
			       struct v4l2_subdev_state *sd_state,
			       struct v4l2_subdev_format *format)
{}

static int tda1997x_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{}

static int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{}

static int tda1997x_get_dv_timings_cap(struct v4l2_subdev *sd,
				       struct v4l2_dv_timings_cap *cap)
{}

static int tda1997x_enum_dv_timings(struct v4l2_subdev *sd,
				    struct v4l2_enum_dv_timings *timings)
{}

static const struct v4l2_subdev_pad_ops tda1997x_pad_ops =;

/* -----------------------------------------------------------------------------
 * v4l2_subdev_core_ops
 */

static int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr)
{}

static int tda1997x_log_status(struct v4l2_subdev *sd)
{}

static int tda1997x_subscribe_event(struct v4l2_subdev *sd,
				    struct v4l2_fh *fh,
				    struct v4l2_event_subscription *sub)
{}

static const struct v4l2_subdev_core_ops tda1997x_core_ops =;

/* -----------------------------------------------------------------------------
 * v4l2_subdev_ops
 */

static const struct v4l2_subdev_ops tda1997x_subdev_ops =;

static const struct v4l2_subdev_internal_ops tda1997x_internal_ops =;

/* -----------------------------------------------------------------------------
 * v4l2_controls
 */

static int tda1997x_s_ctrl(struct v4l2_ctrl *ctrl)
{
	struct v4l2_subdev *sd = to_sd(ctrl);
	struct tda1997x_state *state = to_state(sd);

	switch (ctrl->id) {
	/* allow overriding the default RGB quantization range */
	case V4L2_CID_DV_RX_RGB_RANGE:
		state->rgb_quantization_range = ctrl->val;
		set_rgb_quantization_range(state);
		tda1997x_configure_csc(sd);
		return 0;
	}

	return -EINVAL;
};

static int tda1997x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
	struct v4l2_subdev *sd = to_sd(ctrl);
	struct tda1997x_state *state = to_state(sd);

	if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) {
		ctrl->val = state->avi_infoframe.content_type;
		return 0;
	}
	return -EINVAL;
};

static const struct v4l2_ctrl_ops tda1997x_ctrl_ops =;

static int tda1997x_core_init(struct v4l2_subdev *sd)
{}

static int tda1997x_set_power(struct tda1997x_state *state, bool on)
{}

static const struct i2c_device_id tda1997x_i2c_id[] =;
MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id);

static const struct of_device_id tda1997x_of_id[] __maybe_unused =;
MODULE_DEVICE_TABLE(of, tda1997x_of_id);

static int tda1997x_parse_dt(struct tda1997x_state *state)
{}

static int tda1997x_get_regulators(struct tda1997x_state *state)
{}

static int tda1997x_identify_module(struct tda1997x_state *state)
{}

static const struct media_entity_operations tda1997x_media_ops =;


/* -----------------------------------------------------------------------------
 * HDMI Audio Codec
 */

/* refine sample-rate based on HDMI source */
static int tda1997x_pcm_startup(struct snd_pcm_substream *substream,
				struct snd_soc_dai *dai)
{}

static const struct snd_soc_dai_ops tda1997x_dai_ops =;

static struct snd_soc_dai_driver tda1997x_audio_dai =;

static int tda1997x_codec_probe(struct snd_soc_component *component)
{}

static void tda1997x_codec_remove(struct snd_soc_component *component)
{}

static const struct snd_soc_component_driver tda1997x_codec_driver =;

static int tda1997x_probe(struct i2c_client *client)
{}

static void tda1997x_remove(struct i2c_client *client)
{}

static struct i2c_driver tda1997x_i2c_driver =;

module_i2c_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();