823 lines
20 KiB
C
823 lines
20 KiB
C
/*
|
|
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
|
*/
|
|
|
|
/*
|
|
* 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.
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/media-bus-format.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <linux/videodev2.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include "mxc_vadc.h"
|
|
|
|
/* Resource names for the VADC driver. */
|
|
#define VAFE_REGS_ADDR_RES_NAME "vadc-vafe"
|
|
#define VDEC_REGS_ADDR_RES_NAME "vadc-vdec"
|
|
|
|
#define reg32_write(addr, val) __raw_writel(val, addr)
|
|
#define reg32_read(addr) __raw_readl(addr)
|
|
#define reg32setbit(addr, bitpos) \
|
|
reg32_write((addr), (reg32_read((addr)) | (1<<(bitpos))))
|
|
|
|
#define reg32clrbit(addr, bitpos) \
|
|
reg32_write((addr), (reg32_read((addr)) & (0xFFFFFFFF ^ (1<<(bitpos)))))
|
|
|
|
#define GPC_CNTR 0x00
|
|
#define IMX6SX_GPC_CNTR_VADC_ANALOG_OFF_MASK BIT(17)
|
|
#define IMX6SX_GPC_CNTR_VADC_POWER_DOWN_MASK BIT(18)
|
|
|
|
void __iomem *vafe_regbase;
|
|
void __iomem *vdec_regbase;
|
|
|
|
|
|
/* List of input video formats supported. The video formats is corresponding
|
|
* with v4l2 id in video_fmt
|
|
*/
|
|
enum video_fmt_idx {
|
|
VADC_NTSC = 0, /* Locked on (M) NTSC video signal. */
|
|
VADC_PAL, /* (B, G, H, I, N)PAL video signal. */
|
|
};
|
|
|
|
/* Number of video standards supported (including 'not locked' signal). */
|
|
#define VADC_STD_MAX (VADC_PAL + 1)
|
|
|
|
/* Video format structure. */
|
|
struct video_fmt{
|
|
v4l2_std_id v4l2_std; /* Video for linux ID. */
|
|
char name[16]; /* Name (e.g., "NTSC", "PAL", etc.) */
|
|
u16 raw_width; /* Raw width. */
|
|
u16 raw_height; /* Raw height. */
|
|
u16 active_width; /* Active width. */
|
|
u16 active_height; /* Active height. */
|
|
u16 framerates;
|
|
};
|
|
|
|
/*
|
|
* Maintains the information on the current state of the sensor.
|
|
*/
|
|
struct vadc_state {
|
|
struct v4l2_device v4l2_dev;
|
|
struct v4l2_subdev sd;
|
|
struct video_fmt *fmt;
|
|
|
|
struct clk *vadc_clk;
|
|
struct clk *csi_clk;
|
|
struct regmap *gpr;
|
|
void __iomem *gpc_reg;
|
|
|
|
u32 vadc_in;
|
|
u32 csi_id;
|
|
};
|
|
|
|
static int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std);
|
|
|
|
/* Description of video formats supported.
|
|
*
|
|
* PAL: raw=720x625, active=720x576.
|
|
* NTSC: raw=720x525, active=720x480.
|
|
*/
|
|
static struct video_fmt video_fmts[] = {
|
|
/* NTSC */
|
|
{
|
|
.v4l2_std = V4L2_STD_NTSC,
|
|
.name = "NTSC",
|
|
.raw_width = 720,
|
|
.raw_height = 525,
|
|
.active_width = 720,
|
|
.active_height = 480,
|
|
.framerates = 30,
|
|
},
|
|
/* (B, G, H, I, N) PAL */
|
|
{
|
|
.v4l2_std = V4L2_STD_PAL,
|
|
.name = "PAL",
|
|
.raw_width = 720,
|
|
.raw_height = 625,
|
|
.active_width = 720,
|
|
.active_height = 576,
|
|
.framerates = 25,
|
|
},
|
|
};
|
|
|
|
static void afe_voltage_clampingmode(void)
|
|
{
|
|
reg32_write(AFE_CLAMP, 0x07);
|
|
reg32_write(AFE_CLMPAMP, 0x60);
|
|
reg32_write(AFE_CLMPDAT, 0xF0);
|
|
}
|
|
|
|
static void afe_alwayson_clampingmode(void)
|
|
{
|
|
reg32_write(AFE_CLAMP, 0x15);
|
|
reg32_write(AFE_CLMPDAT, 0x08);
|
|
reg32_write(AFE_CLMPAMP, 0x00);
|
|
}
|
|
|
|
static void afe_init(void)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
|
|
reg32_write(AFE_PDBUF, 0x1f);
|
|
reg32_write(AFE_PDADC, 0x0f);
|
|
reg32_write(AFE_PDSARH, 0x01);
|
|
reg32_write(AFE_PDSARL, 0xff);
|
|
reg32_write(AFE_PDADCRFH, 0x01);
|
|
reg32_write(AFE_PDADCRFL, 0xff);
|
|
reg32_write(AFE_ICTRL, 0x3a);
|
|
reg32_write(AFE_ICTLSTG, 0x1e);
|
|
|
|
reg32_write(AFE_RCTRLSTG, 0x1e);
|
|
reg32_write(AFE_INPBUF, 0x035);
|
|
reg32_write(AFE_INPFLT, 0x02);
|
|
reg32_write(AFE_ADCDGN, 0x40);
|
|
reg32_write(AFE_TSTSEL, 0x10);
|
|
|
|
reg32_write(AFE_ACCTST, 0x07);
|
|
|
|
reg32_write(AFE_BGREG, 0x08);
|
|
|
|
reg32_write(AFE_ADCGN, 0x09);
|
|
|
|
/* set current controlled clamping
|
|
* always on, low current */
|
|
reg32_write(AFE_CLAMP, 0x11);
|
|
reg32_write(AFE_CLMPAMP, 0x08);
|
|
}
|
|
|
|
static void vdec_mode_timing_init(int std)
|
|
{
|
|
if (std == V4L2_STD_NTSC) {
|
|
/* NTSC 720x480 */
|
|
reg32_write(VDEC_HACTS, 0x66);
|
|
reg32_write(VDEC_HACTE, 0x24);
|
|
|
|
reg32_write(VDEC_VACTS, 0x29);
|
|
reg32_write(VDEC_VACTE, 0x04);
|
|
|
|
/* set V Position */
|
|
reg32_write(VDEC_VRTPOS, 0x2);
|
|
} else if (std == V4L2_STD_PAL) {
|
|
/* PAL 720x576 */
|
|
reg32_write(VDEC_HACTS, 0x66);
|
|
reg32_write(VDEC_HACTE, 0x24);
|
|
|
|
reg32_write(VDEC_VACTS, 0x29);
|
|
reg32_write(VDEC_VACTE, 0x04);
|
|
|
|
/* set V Position */
|
|
reg32_write(VDEC_VRTPOS, 0x6);
|
|
} else
|
|
pr_debug("Error not support video mode\n");
|
|
|
|
/* set H Position */
|
|
reg32_write(VDEC_HZPOS, 0x60);
|
|
|
|
/* set H ignore start */
|
|
reg32_write(VDEC_HSIGS, 0xf8);
|
|
|
|
/* set H ignore end */
|
|
reg32_write(VDEC_HSIGE, 0x18);
|
|
}
|
|
|
|
/*
|
|
* vdec_init()
|
|
* Initialises the VDEC registers
|
|
* Returns: nothing
|
|
*/
|
|
static void vdec_init(struct vadc_state *vadc)
|
|
{
|
|
v4l2_std_id std;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
/* Get work mode PAL or NTSC */
|
|
vadc_querystd(&vadc->sd, &std);
|
|
|
|
vdec_mode_timing_init(std);
|
|
|
|
/* vcr detect threshold high, automatic detections */
|
|
reg32_write(VDEC_VSCON2, 0);
|
|
|
|
reg32_write(VDEC_BASE + 0x110, 0x01);
|
|
|
|
/* set the noramp mode on the Hloop PLL. */
|
|
reg32_write(VDEC_BASE+(0x14*4), 0x10);
|
|
|
|
/* set the YC relative delay.*/
|
|
reg32_write(VDEC_YCDEL, 0x90);
|
|
|
|
/* setup the Hpll */
|
|
reg32_write(VDEC_BASE+(0x13*4), 0x13);
|
|
|
|
/* setup the 2d comb */
|
|
/* set the gain of the Hdetail output to 3
|
|
* set the notch alpha gain to 1 */
|
|
reg32_write(VDEC_CFC2, 0x34);
|
|
|
|
/* setup various 2d comb bits.*/
|
|
reg32_write(VDEC_BASE+(0x02*4), 0x01);
|
|
reg32_write(VDEC_BASE+(0x03*4), 0x18);
|
|
reg32_write(VDEC_BASE+(0x04*4), 0x34);
|
|
|
|
/* set the start of the burst gate */
|
|
reg32_write(VDEC_BRSTGT, 0x30);
|
|
|
|
/* set 1f motion gain */
|
|
reg32_write(VDEC_BASE+(0x0f*4), 0x20);
|
|
|
|
/* set the 1F chroma motion detector thresh
|
|
* for colour reverse detection */
|
|
reg32_write(VDEC_THSH1, 0x02);
|
|
reg32_write(VDEC_BASE+(0x4a*4), 0x20);
|
|
reg32_write(VDEC_BASE+(0x4b*4), 0x08);
|
|
|
|
reg32_write(VDEC_BASE+(0x4c*4), 0x08);
|
|
|
|
/* set the threshold for the narrow/wide adaptive chroma BW */
|
|
reg32_write(VDEC_BASE+(0x20*4), 0x20);
|
|
|
|
/* turn up the colour with the new colour gain reg */
|
|
/* hue: */
|
|
reg32_write(VDEC_HUE, 0x00);
|
|
|
|
/* cbgain: 22 B4 */
|
|
reg32_write(VDEC_CBGN, 0xb4);
|
|
/* cr gain 80 */
|
|
reg32_write(VDEC_CRGN, 0x80);
|
|
/* luma gain (contrast) */
|
|
reg32_write(VDEC_CNTR, 0x80);
|
|
|
|
/* setup the signed black level register, brightness */
|
|
reg32_write(VDEC_BRT, 0x00);
|
|
|
|
/* filter the standard detection
|
|
* enable the comb for the ntsc443 */
|
|
reg32_write(VDEC_STDDBG, 0x20);
|
|
|
|
/* setup chroma kill thresh for no chroma */
|
|
reg32_write(VDEC_CHBTH, 0x0);
|
|
|
|
/* set chroma loop to wider BW
|
|
* no set it to normal BW. i fixed the bw problem.*/
|
|
reg32_write(VDEC_YCDEL, 0x00);
|
|
|
|
/* set the compensation in the chroma loop for the Hloop
|
|
* set the ratio for the nonarithmetic 3d comb modes.*/
|
|
reg32_write(VDEC_BASE + (0x1d*4), 0x90);
|
|
|
|
/* set the threshold for the nonarithmetic mode for the 2d comb
|
|
* the higher the value the more Fc Fh offset
|
|
* we will tolerate before turning off the comb. */
|
|
reg32_write(VDEC_BASE + (0x33*4), 0xa0);
|
|
|
|
/* setup the bluescreen output colour */
|
|
reg32_write(VDEC_BASE + (0x3d*4), 35);
|
|
reg32_write(VDEC_BLSCRCR, 114);
|
|
reg32_write(VDEC_BLSCRCB, 212);
|
|
|
|
/* disable the active blanking */
|
|
reg32_write(VDEC_BASE + (0x15*4), 0x02);
|
|
|
|
/* setup the luma agc for automatic gain. */
|
|
reg32_write(VDEC_LMAGC2, 0x5e);
|
|
reg32_write(VDEC_LMAGC1, 0x81);
|
|
|
|
/* setup chroma agc */
|
|
reg32_write(VDEC_CHAGC2, 0xa0);
|
|
reg32_write(VDEC_CHAGC1, 0x01);
|
|
|
|
/* setup the MV thresh lower nibble
|
|
* setup the sync top cap, upper nibble */
|
|
reg32_write(VDEC_BASE + (0x3a*4), 0x80);
|
|
reg32_write(VDEC_SHPIMP, 0x00);
|
|
|
|
/* setup the vsync block */
|
|
reg32_write(VDEC_VSCON1, 0x87);
|
|
|
|
/* set the nosignal threshold
|
|
* set the vsync threshold */
|
|
reg32_write(VDEC_VSSGTH, 0x35);
|
|
|
|
/* set length for min hphase filter
|
|
* (or saturate limit if saturate is chosen) */
|
|
reg32_write(VDEC_BASE + (0x45*4), 0x40);
|
|
|
|
/* enable the internal resampler,
|
|
* select min filter not saturate for
|
|
* hphase noise filter for vcr detect.
|
|
* enable vcr pause mode different field lengths */
|
|
reg32_write(VDEC_BASE + (0x46*4), 0x90);
|
|
|
|
/* disable VCR detection, lock to the Hsync rather than the Vsync */
|
|
reg32_write(VDEC_VSCON2, 0x04);
|
|
|
|
/* set tiplevel goal for dc clamp. */
|
|
reg32_write(VDEC_BASE + (0x3c*4), 0xB0);
|
|
|
|
/* override SECAM detection and force SECAM off */
|
|
reg32_write(VDEC_BASE + (0x2f*4), 0x20);
|
|
|
|
/* Set r3d_hardblend in 3D control2 reg */
|
|
reg32_write(VDEC_BASE + (0x0c*4), 0x04);
|
|
}
|
|
|
|
/* set Input selector & input pull-downs */
|
|
static void vadc_s_routing(int vadc_in)
|
|
{
|
|
switch (vadc_in) {
|
|
case 0:
|
|
reg32_write(AFE_INPFLT, 0x02);
|
|
reg32_write(AFE_OFFDRV, 0x00);
|
|
reg32_write(AFE_INPCONFIG, 0x1e);
|
|
break;
|
|
case 1:
|
|
reg32_write(AFE_INPFLT, 0x02);
|
|
reg32_write(AFE_OFFDRV, 0x00);
|
|
reg32_write(AFE_INPCONFIG, 0x2d);
|
|
break;
|
|
case 2:
|
|
reg32_write(AFE_INPFLT, 0x02);
|
|
reg32_write(AFE_OFFDRV, 0x00);
|
|
reg32_write(AFE_INPCONFIG, 0x4b);
|
|
break;
|
|
case 3:
|
|
reg32_write(AFE_INPFLT, 0x02);
|
|
reg32_write(AFE_OFFDRV, 0x00);
|
|
reg32_write(AFE_INPCONFIG, 0x87);
|
|
break;
|
|
default:
|
|
pr_debug("error video input %d\n", vadc_in);
|
|
}
|
|
}
|
|
|
|
static void vadc_power_up(struct vadc_state *state)
|
|
{
|
|
/* Power on vadc analog */
|
|
reg32clrbit(state->gpc_reg + GPC_CNTR, 17);
|
|
|
|
/* Power down vadc ext power */
|
|
reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
|
|
|
|
/* software reset afe */
|
|
regmap_update_bits(state->gpr, IOMUXC_GPR1,
|
|
IMX6SX_GPR1_VADC_SW_RST_MASK,
|
|
IMX6SX_GPR1_VADC_SW_RST_RESET);
|
|
|
|
msleep(10);
|
|
|
|
/* clock config for vadc */
|
|
reg32_write(VDEC_BASE + 0x320, 0xe3);
|
|
reg32_write(VDEC_BASE + 0x324, 0x38);
|
|
reg32_write(VDEC_BASE + 0x328, 0x8e);
|
|
reg32_write(VDEC_BASE + 0x32c, 0x23);
|
|
|
|
/* Release reset bit */
|
|
regmap_update_bits(state->gpr, IOMUXC_GPR1,
|
|
IMX6SX_GPR1_VADC_SW_RST_MASK,
|
|
IMX6SX_GPR1_VADC_SW_RST_RELEASE);
|
|
|
|
/* Power on vadc ext power */
|
|
reg32setbit(state->gpc_reg + GPC_CNTR, 18);
|
|
}
|
|
|
|
static void vadc_power_down(struct vadc_state *state)
|
|
{
|
|
/* Power down vadc analog */
|
|
reg32setbit(state->gpc_reg + GPC_CNTR, 17);
|
|
|
|
/* Power down vadc ext power */
|
|
reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
|
|
|
|
}
|
|
static void vadc_init(struct vadc_state *vadc)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
|
|
vadc_power_up(vadc);
|
|
|
|
afe_init();
|
|
|
|
/* select Video Input 0-3 */
|
|
vadc_s_routing(vadc->vadc_in);
|
|
|
|
afe_voltage_clampingmode();
|
|
|
|
vdec_init(vadc);
|
|
|
|
/*
|
|
* current control loop will move sinewave input off below
|
|
* the bottom of the signal range visible
|
|
* when the testbus is viewed as magnitude,
|
|
* so have to break before this point while capturing ENOB data:
|
|
*/
|
|
afe_alwayson_clampingmode();
|
|
}
|
|
|
|
static inline struct vadc_state *to_state(struct v4l2_subdev *sd)
|
|
{
|
|
return container_of(sd, struct vadc_state, sd);
|
|
}
|
|
|
|
static int vadc_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
|
|
{
|
|
struct vadc_state *state = to_state(sd);
|
|
|
|
*std = state->fmt->v4l2_std;
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* Return attributes of current video standard.
|
|
* Since this device autodetects the current standard, this function also
|
|
* sets the values that need to be changed if the standard changes.
|
|
* There is no set std equivalent function.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
|
|
{
|
|
struct vadc_state *state = to_state(sd);
|
|
int mod;
|
|
int idx;
|
|
int i;
|
|
|
|
/* Read auto mode detected result */
|
|
printk(KERN_INFO"wait vadc auto detect video mode....\n");
|
|
for (i = 0; i < 10; i++) {
|
|
msleep(200);
|
|
mod = reg32_read(VDEC_VIDMOD);
|
|
/* Check video signal states */
|
|
if ((mod & VDEC_VIDMOD_SIGNAL_MASK)
|
|
== VDEC_VIDMOD_SIGNAL_DETECT)
|
|
break;
|
|
}
|
|
if (i == 10)
|
|
printk(KERN_INFO"Timeout detect video signal mod=0x%x\n", mod);
|
|
|
|
if ((mod & VDEC_VIDMOD_PAL_MASK) || (mod & VDEC_VIDMOD_M625_MASK))
|
|
idx = VADC_PAL;
|
|
else
|
|
idx = VADC_NTSC;
|
|
|
|
*std = video_fmts[idx].v4l2_std;
|
|
state->fmt = &video_fmts[idx];
|
|
|
|
printk(KERN_INFO"video mode %s\n", video_fmts[idx].name);
|
|
return 0;
|
|
}
|
|
|
|
static int vadc_enum_mbus_code(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_mbus_code_enum *code)
|
|
{
|
|
/* support only one format */
|
|
if (code->pad || code->index >= 1)
|
|
return -EINVAL;
|
|
|
|
code->code = MEDIA_BUS_FMT_AYUV8_1X32;
|
|
return 0;
|
|
}
|
|
|
|
static int vadc_get_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_format *format)
|
|
{
|
|
struct vadc_state *state = to_state(sd);
|
|
struct v4l2_mbus_framefmt *fmt = &format->format;
|
|
|
|
if (format->pad)
|
|
return -EINVAL;
|
|
|
|
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
|
|
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
fmt->field = V4L2_FIELD_INTERLACED;
|
|
fmt->width = 720;
|
|
fmt->height = state->fmt->v4l2_std & V4L2_STD_NTSC ? 480 : 576;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vadc_enum_framesizes(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_frame_size_enum *fse)
|
|
{
|
|
struct vadc_state *state = to_state(sd);
|
|
if (fse->index >= 1)
|
|
return -EINVAL;
|
|
|
|
fse->min_width = state->fmt->active_width;
|
|
fse->max_width = state->fmt->active_width;
|
|
fse->min_height = state->fmt->active_height;
|
|
fse->max_height = state->fmt->active_height;
|
|
|
|
return 0;
|
|
}
|
|
static int vadc_enum_frameintervals(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_frame_interval_enum *fie)
|
|
{
|
|
struct vadc_state *state = to_state(sd);
|
|
|
|
if (fie->index < 0 || fie->index >= 1)
|
|
return -EINVAL;
|
|
|
|
fie->interval.numerator = 1;
|
|
|
|
fie->interval.denominator = state->fmt->framerates;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vadc_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
|
|
{
|
|
struct vadc_state *state = to_state(sd);
|
|
|
|
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
return -EINVAL;
|
|
|
|
if (parms->parm.capture.timeperframe.denominator
|
|
!= state->fmt->framerates)
|
|
parms->parm.capture.timeperframe.denominator
|
|
= state->fmt->framerates;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_subdev_video_ops vadc_video_ops = {
|
|
.querystd = vadc_querystd,
|
|
.s_parm = vadc_s_parm,
|
|
.g_std = vadc_g_std,
|
|
};
|
|
|
|
|
|
static const struct v4l2_subdev_pad_ops vadc_pad_ops = {
|
|
.get_fmt = vadc_get_fmt,
|
|
.enum_mbus_code = vadc_enum_mbus_code,
|
|
.enum_frame_size = vadc_enum_framesizes,
|
|
.enum_frame_interval = vadc_enum_frameintervals,
|
|
};
|
|
|
|
static const struct v4l2_subdev_ops vadc_ops = {
|
|
.video = &vadc_video_ops,
|
|
.pad = &vadc_pad_ops,
|
|
};
|
|
|
|
static const struct of_device_id fsl_vadc_dt_ids[] = {
|
|
{ .compatible = "fsl,imx6sx-vadc", },
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, fsl_vadc_dt_ids);
|
|
|
|
static int vadc_of_init(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct device_node *gpc_np;
|
|
struct vadc_state *state = platform_get_drvdata(pdev);
|
|
int csi_id;
|
|
int ret;
|
|
|
|
/* Get csi_id to setting vadc to csi mux in gpr */
|
|
ret = of_property_read_u32(np, "csi_id", &csi_id);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to read of property csi_id\n");
|
|
return ret;
|
|
}
|
|
|
|
state->csi_id = csi_id;
|
|
|
|
/* remap GPR register */
|
|
state->gpr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
|
"gpr");
|
|
if (IS_ERR(state->gpr)) {
|
|
dev_dbg(&pdev->dev, "can not get gpr\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Configuration vadc-to-csi 0 or 1 */
|
|
if (csi_id) {
|
|
regmap_update_bits(state->gpr, IOMUXC_GPR5,
|
|
IMX6SX_GPR5_CSI2_MUX_CTRL_MASK,
|
|
IMX6SX_GPR5_CSI2_MUX_CTRL_CVD);
|
|
} else {
|
|
regmap_update_bits(state->gpr, IOMUXC_GPR5,
|
|
IMX6SX_GPR5_CSI1_MUX_CTRL_MASK,
|
|
IMX6SX_GPR5_CSI1_MUX_CTRL_CVD);
|
|
}
|
|
|
|
/* Get default vadc_in number */
|
|
ret = of_property_read_u32(np, "vadc_in", &state->vadc_in);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to read of property vadc_in\n");
|
|
return ret;
|
|
}
|
|
|
|
/* map GPC register */
|
|
gpc_np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc");
|
|
state->gpc_reg = of_iomap(gpc_np, 0);
|
|
if (!state->gpc_reg) {
|
|
dev_err(&pdev->dev, "ioremap failed with gpc base\n");
|
|
goto error;
|
|
}
|
|
|
|
return ret;
|
|
|
|
error:
|
|
iounmap(state->gpc_reg);
|
|
return ret;
|
|
}
|
|
|
|
static void vadc_v4l2_subdev_init(struct v4l2_subdev *sd,
|
|
struct platform_device *pdev,
|
|
const struct v4l2_subdev_ops *ops)
|
|
{
|
|
struct vadc_state *state = platform_get_drvdata(pdev);
|
|
int ret = 0;
|
|
|
|
v4l2_subdev_init(sd, ops);
|
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
sd->owner = pdev->dev.driver->owner;
|
|
sd->dev = &pdev->dev;
|
|
|
|
/* initialize name */
|
|
snprintf(sd->name, sizeof(sd->name), "%s",
|
|
pdev->dev.driver->name);
|
|
|
|
v4l2_set_subdevdata(sd, state);
|
|
|
|
ret = v4l2_async_register_subdev(sd);
|
|
if (ret < 0)
|
|
dev_err(&pdev->dev, "%s--Async register faialed, ret=%d\n", __func__, ret);
|
|
}
|
|
|
|
static int vadc_probe(struct platform_device *pdev)
|
|
{
|
|
struct vadc_state *state;
|
|
struct v4l2_subdev *sd;
|
|
struct resource *res;
|
|
int ret = 0;
|
|
|
|
state = devm_kzalloc(&pdev->dev, sizeof(struct vadc_state), GFP_KERNEL);
|
|
if (!state) {
|
|
dev_err(&pdev->dev, "Cannot allocate device data\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Set initial values for the sensor struct. */
|
|
state->fmt = &video_fmts[VADC_NTSC];
|
|
|
|
sd = &state->sd;
|
|
|
|
/* map vafe address */
|
|
res = platform_get_resource_byname(pdev,
|
|
IORESOURCE_MEM, VAFE_REGS_ADDR_RES_NAME);
|
|
if (!res) {
|
|
dev_err(&pdev->dev, "No vafe base address found.\n");
|
|
return -ENOMEM;
|
|
}
|
|
vafe_regbase = devm_ioremap_resource(&pdev->dev, res);
|
|
if (!vafe_regbase) {
|
|
dev_err(&pdev->dev, "ioremap failed with vafe base\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* map vdec address */
|
|
res = platform_get_resource_byname(pdev,
|
|
IORESOURCE_MEM, VDEC_REGS_ADDR_RES_NAME);
|
|
if (!res) {
|
|
dev_err(&pdev->dev, "No vdec base address found.\n");
|
|
return -ENODEV;
|
|
}
|
|
vdec_regbase = devm_ioremap_resource(&pdev->dev, res);
|
|
if (!vdec_regbase) {
|
|
dev_err(&pdev->dev, "ioremap failed with vdec base\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Get clock */
|
|
state->vadc_clk = devm_clk_get(&pdev->dev, "vadc");
|
|
if (IS_ERR(state->vadc_clk)) {
|
|
ret = PTR_ERR(state->vadc_clk);
|
|
return ret;
|
|
}
|
|
|
|
state->csi_clk = devm_clk_get(&pdev->dev, "csi");
|
|
if (IS_ERR(state->csi_clk)) {
|
|
ret = PTR_ERR(state->csi_clk);
|
|
return ret;
|
|
}
|
|
|
|
/* clock */
|
|
clk_prepare_enable(state->csi_clk);
|
|
clk_prepare_enable(state->vadc_clk);
|
|
|
|
platform_set_drvdata(pdev, state);
|
|
|
|
vadc_v4l2_subdev_init(sd, pdev, &vadc_ops);
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
pm_runtime_get_sync(&pdev->dev);
|
|
/* Init VADC */
|
|
ret = vadc_of_init(pdev);
|
|
if (ret < 0)
|
|
goto err;
|
|
vadc_init(state);
|
|
|
|
pr_info("vadc driver loaded\n");
|
|
|
|
return 0;
|
|
err:
|
|
pm_runtime_put_sync(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
v4l2_async_unregister_subdev(&state->sd);
|
|
clk_disable_unprepare(state->csi_clk);
|
|
clk_disable_unprepare(state->vadc_clk);
|
|
return ret;
|
|
}
|
|
|
|
static int vadc_remove(struct platform_device *pdev)
|
|
{
|
|
struct vadc_state *state = platform_get_drvdata(pdev);
|
|
|
|
pm_runtime_put_sync(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
v4l2_async_unregister_subdev(&state->sd);
|
|
clk_disable_unprepare(state->csi_clk);
|
|
clk_disable_unprepare(state->vadc_clk);
|
|
|
|
vadc_power_down(state);
|
|
return true;
|
|
}
|
|
|
|
static int vadc_suspend(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct vadc_state *state = platform_get_drvdata(pdev);
|
|
|
|
clk_disable(state->csi_clk);
|
|
clk_disable(state->vadc_clk);
|
|
|
|
vadc_power_down(state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vadc_resume(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct vadc_state *state = platform_get_drvdata(pdev);
|
|
|
|
clk_enable(state->csi_clk);
|
|
clk_enable(state->vadc_clk);
|
|
|
|
vadc_init(state);
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops vadc_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(vadc_suspend, vadc_resume)
|
|
};
|
|
|
|
static struct platform_driver vadc_driver = {
|
|
.driver = {
|
|
.name = "fsl_vadc",
|
|
.of_match_table = of_match_ptr(fsl_vadc_dt_ids),
|
|
.pm = &vadc_pm_ops,
|
|
},
|
|
.probe = vadc_probe,
|
|
.remove = vadc_remove,
|
|
};
|
|
|
|
module_platform_driver(vadc_driver);
|
|
|
|
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
|
MODULE_DESCRIPTION("fsl VADC/VDEC driver");
|
|
MODULE_LICENSE("GPL");
|