| /* |
| * 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_set_fmt(struct v4l2_subdev *sd, |
| struct v4l2_subdev_pad_config *cfg, |
| struct v4l2_subdev_format *format) |
| { |
| return vadc_get_fmt(sd, cfg, format); |
| } |
| |
| 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, |
| .set_fmt = vadc_set_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"); |