@@ -9,6 +9,7 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/of_graph.h>
@@ -44,12 +45,14 @@ struct tvp5150 {
struct v4l2_mbus_framefmt format;
struct v4l2_rect rect;
struct regmap *regmap;
+ int irq;
v4l2_std_id norm; /* Current set standard */
v4l2_std_id detected_norm;
u32 input;
u32 output;
int enable;
+ bool lock;
};
static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
@@ -716,6 +719,15 @@ static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
return 0;
}
+static int tvp5150_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ *std = decoder->norm;
+
+ return 0;
+}
+
static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct tvp5150 *decoder = to_tvp5150(sd);
@@ -758,14 +770,25 @@ static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ struct regmap *map = decoder->regmap;
+
/* Initializes TVP5150 to its default values */
tvp5150_write_inittab(sd, tvp5150_init_default);
- /* Configure pins: FID, VSYNC, GPCL/VBLK, SCLK */
- regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x2);
- /* Keep interrupt polarity active low */
- regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE);
- regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x0);
+ if (decoder->irq) {
+ /* Configure pins: FID, VSYNC, INTREQ, SCLK */
+ regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x0);
+ /* Set interrupt polarity to active high */
+ regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE | 0x1);
+ regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x1);
+ } else {
+ /* Configure pins: FID, VSYNC, GPCL/VBLK, SCLK */
+ regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x2);
+ /* Keep interrupt polarity active low */
+ regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE);
+ regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x0);
+ }
/* Initializes VDP registers */
tvp5150_vdp_init(sd, vbi_ram_default);
@@ -776,6 +799,33 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
return 0;
}
+static irqreturn_t tvp5150_isr(int irq, void *dev_id)
+{
+ struct tvp5150 *decoder = dev_id;
+ struct regmap *map = decoder->regmap;
+ unsigned int active = 0, status = 0;
+
+ regmap_read(map, TVP5150_INT_STATUS_REG_A, &status);
+ if (status) {
+ regmap_write(map, TVP5150_INT_STATUS_REG_A, status);
+
+ if (status & TVP5150_INT_A_LOCK)
+ decoder->lock = !!(status & TVP5150_INT_A_LOCK_STATUS);
+
+ return IRQ_HANDLED;
+ }
+
+ regmap_read(map, TVP5150_INT_ACTIVE_REG_B, &active);
+ if (active) {
+ status = 0;
+ regmap_read(map, TVP5150_INT_STATUS_REG_B, &status);
+ if (status)
+ regmap_write(map, TVP5150_INT_RESET_REG_B, status);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int tvp5150_enable(struct v4l2_subdev *sd)
{
struct tvp5150 *decoder = to_tvp5150(sd);
@@ -939,6 +989,35 @@ static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
return 0;
}
+static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+
+ if (enable) {
+ /* Enable YUV(OUT7:0), clock */
+ regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, 0xd,
+ (decoder->bus_type == V4L2_MBUS_BT656) ? 0x9 : 0xd);
+ if (decoder->irq) {
+ /* Enable lock interrupt */
+ regmap_update_bits(decoder->regmap,
+ TVP5150_INT_ENABLE_REG_A,
+ TVP5150_INT_A_LOCK,
+ TVP5150_INT_A_LOCK);
+ }
+ } else {
+ /* Disable YUV(OUT7:0), SYNC, clock */
+ regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, 0xd, 0x0);
+ if (decoder->irq) {
+ /* Disable lock interrupt */
+ regmap_update_bits(decoder->regmap,
+ TVP5150_INT_ENABLE_REG_A,
+ TVP5150_INT_A_LOCK, 0);
+ }
+ }
+
+ return 0;
+}
+
static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
struct tvp5150 *decoder = to_tvp5150(sd);
@@ -1239,9 +1318,11 @@ static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
.s_std = tvp5150_s_std,
+ .g_std = tvp5150_g_std,
.s_routing = tvp5150_s_routing,
.s_crop = tvp5150_s_crop,
.g_crop = tvp5150_g_crop,
+ .s_stream = tvp5150_s_stream,
.cropcap = tvp5150_cropcap,
};
@@ -1442,7 +1523,19 @@ static int tvp5150_probe(struct i2c_client *c,
}
v4l2_ctrl_handler_setup(&core->hdl);
+ core->irq = c->irq;
tvp5150_reset(sd, 0);
+
+ if (c->irq) {
+ res = devm_request_threaded_irq(&c->dev, c->irq, NULL,
+ tvp5150_isr, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "tvp5150", core);
+ if (res)
+ return res;
+ } else {
+ core->lock = true;
+ }
+
/* Default is no cropping */
tvp5150_set_default(tvp5150_read_std(sd), &core->rect, &core->format);
@@ -115,6 +115,8 @@
#define TVP5150_TELETEXT_FIL_ENA 0xbb /* Teletext filter enable */
/* Reserved BCh-BFh */
#define TVP5150_INT_STATUS_REG_A 0xc0 /* Interrupt status register A */
+#define TVP5150_INT_A_LOCK_STATUS BIT(7)
+#define TVP5150_INT_A_LOCK BIT(6)
#define TVP5150_INT_ENABLE_REG_A 0xc1 /* Interrupt enable register A */
#define TVP5150_INT_CONF 0xc2 /* Interrupt configuration */
#define TVP5150_VDPOE BIT(2)