diff options
122 files changed, 7425 insertions, 7051 deletions
diff --git a/Documentation/DocBook/media/v4l/media-types.xml b/Documentation/DocBook/media/v4l/media-types.xml index 5e3f20fdcf17..95aa1f9c836a 100644 --- a/Documentation/DocBook/media/v4l/media-types.xml +++ b/Documentation/DocBook/media/v4l/media-types.xml @@ -121,6 +121,70 @@ <entry><constant>MEDIA_ENT_F_AUDIO_MIXER</constant></entry> <entry>Audio Mixer Function Entity.</entry> </row> + <row> + <entry><constant>MEDIA_ENT_F_PROC_VIDEO_COMPOSER</constant></entry> + <entry>Video composer (blender). An entity capable of video + composing must have at least two sink pads and one source + pad, and composes input video frames onto output video + frames. Composition can be performed using alpha blending, + color keying, raster operations (ROP), stitching or any other + means. + </entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER</constant></entry> + <entry>Video pixel formatter. An entity capable of pixel formatting + must have at least one sink pad and one source pad. Read + pixel formatters read pixels from memory and perform a subset + of unpacking, cropping, color keying, alpha multiplication + and pixel encoding conversion. Write pixel formatters perform + a subset of dithering, pixel encoding conversion and packing + and write pixels to memory. + </entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV</constant></entry> + <entry>Video pixel encoding converter. An entity capable of pixel + enconding conversion must have at least one sink pad and one + source pad, and convert the encoding of pixels received on + its sink pad(s) to a different encoding output on its source + pad(s). Pixel encoding conversion includes but isn't limited + to RGB to/from HSV, RGB to/from YUV and CFA (Bayer) to RGB + conversions. + </entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_PROC_VIDEO_LUT</constant></entry> + <entry>Video look-up table. An entity capable of video lookup table + processing must have one sink pad and one source pad. It uses + the values of the pixels received on its sink pad to look up + entries in internal tables and output them on its source pad. + The lookup processing can be performed on all components + separately or combine them for multi-dimensional table + lookups. + </entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_PROC_VIDEO_SCALER</constant></entry> + <entry>Video scaler. An entity capable of video scaling must have + at least one sink pad and one source pad, and scale the + video frame(s) received on its sink pad(s) to a different + resolution output on its source pad(s). The range of + supported scaling ratios is entity-specific and can differ + between the horizontal and vertical directions (in particular + scaling can be supported in one direction only). Binning and + skipping are considered as scaling. + </entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_PROC_VIDEO_STATISTICS</constant></entry> + <entry>Video statistics computation (histogram, 3A, ...). An entity + capable of statistics computation must have one sink pad and + one source pad. It computes statistics over the frames + received on its sink pad and outputs the statistics data on + its source pad. + </entry> + </row> </tbody> </tgroup> </table> diff --git a/Documentation/devicetree/bindings/media/mediatek-vpu.txt b/Documentation/devicetree/bindings/media/mediatek-vpu.txt new file mode 100644 index 000000000000..2a5bac37f9a2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/mediatek-vpu.txt @@ -0,0 +1,31 @@ +* Mediatek Video Processor Unit + +Video Processor Unit is a HW video controller. It controls HW Codec including +H.264/VP8/VP9 Decode, H.264/VP8 Encode and Image Processor (scale/rotate/color convert). + +Required properties: + - compatible: "mediatek,mt8173-vpu" + - reg: Must contain an entry for each entry in reg-names. + - reg-names: Must include the following entries: + "tcm": tcm base + "cfg_reg": Main configuration registers base + - interrupts: interrupt number to the cpu. + - clocks : clock name from clock manager + - clock-names: must be main. It is the main clock of VPU + +Optional properties: + - memory-region: phandle to a node describing memory (see + Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt) + to be used for VPU extended memory; if not present, VPU may be located + anywhere in the memory + +Example: + vpu: vpu@10020000 { + compatible = "mediatek,mt8173-vpu"; + reg = <0 0x10020000 0 0x30000>, + <0 0x10050000 0 0x100>; + reg-names = "tcm", "cfg_reg"; + interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&topckgen TOP_SCP_SEL>; + clock-names = "main"; + }; diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.txt b/Documentation/devicetree/bindings/media/renesas,fcp.txt new file mode 100644 index 000000000000..6a12960609d8 --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,fcp.txt @@ -0,0 +1,32 @@ +Renesas R-Car Frame Compression Processor (FCP) +----------------------------------------------- + +The FCP is a companion module of video processing modules in the Renesas R-Car +Gen3 SoCs. It provides data compression and decompression, data caching, and +conversion of AXI transactions in order to reduce the memory bandwidth. + +There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP +for FDP (FCPF). Their configuration and behaviour depend on the module they +are paired with. These DT bindings currently support the FCPV only. + + - compatible: Must be one or more of the following + + - "renesas,r8a7795-fcpv" for R8A7795 (R-Car H3) compatible 'FCP for VSP' + - "renesas,fcpv" for generic compatible 'FCP for VSP' + + When compatible with the generic version, nodes must list the + SoC-specific version corresponding to the platform first, followed by the + family-specific and/or generic versions. + + - reg: the register base and size for the device registers + - clocks: Reference to the functional clock + + +Device node example +------------------- + + fcpvd1: fcp@fea2f000 { + compatible = "renesas,r8a7795-fcpv", "renesas,fcpv"; + reg = <0 0xfea2f000 0 0x200>; + clocks = <&cpg CPG_MOD 602>; + }; diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.txt b/Documentation/devicetree/bindings/media/renesas,vsp1.txt index 627405abd144..9b695bcbf219 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.txt +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.txt @@ -14,6 +14,11 @@ Required properties: - interrupts: VSP interrupt specifier. - clocks: A phandle + clock-specifier pair for the VSP functional clock. +Optional properties: + + - renesas,fcp: A phandle referencing the FCP that handles memory accesses + for the VSP. Not needed on Gen2, mandatory on Gen3. + Example: R8A7790 (R-Car H2) VSP1-S node diff --git a/Documentation/devicetree/bindings/media/s5p-mfc.txt b/Documentation/devicetree/bindings/media/s5p-mfc.txt index 2d5787eac91a..92c94f5ecbf1 100644 --- a/Documentation/devicetree/bindings/media/s5p-mfc.txt +++ b/Documentation/devicetree/bindings/media/s5p-mfc.txt @@ -21,15 +21,18 @@ Required properties: - clock-names : from common clock binding: must contain "mfc", corresponding to entry in the clocks property. - - samsung,mfc-r : Base address of the first memory bank used by MFC - for DMA contiguous memory allocation and its size. - - - samsung,mfc-l : Base address of the second memory bank used by MFC - for DMA contiguous memory allocation and its size. - Optional properties: - power-domains : power-domain property defined with a phandle to respective power domain. + - memory-region : from reserved memory binding: phandles to two reserved + memory regions, first is for "left" mfc memory bus interfaces, + second if for the "right" mfc memory bus, used when no SYSMMU + support is available + +Obsolete properties: + - samsung,mfc-r, samsung,mfc-l : support removed, please use memory-region + property instead + Example: SoC specific DT entry: @@ -43,9 +46,29 @@ mfc: codec@13400000 { clock-names = "mfc"; }; +Reserved memory specific DT entry for given board (see reserved memory binding +for more information): + +reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; +}; + Board specific DT entry: codec@13400000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; diff --git a/MAINTAINERS b/MAINTAINERS index ea8c7ab56cf5..6fc95411d705 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7357,6 +7357,16 @@ L: linux-iio@vger.kernel.org S: Maintained F: drivers/iio/potentiometer/mcp4531.c +MEDIA DRIVERS FOR RENESAS - FCP +M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +L: linux-media@vger.kernel.org +L: linux-renesas-soc@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Supported +F: Documentation/devicetree/bindings/media/renesas,fcp.txt +F: drivers/media/platform/rcar-fcp.c +F: include/media/rcar-fcp.h + MEDIA DRIVERS FOR RENESAS - VSP1 M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> L: linux-media@vger.kernel.org @@ -7366,8 +7376,18 @@ S: Supported F: Documentation/devicetree/bindings/media/renesas,vsp1.txt F: drivers/media/platform/vsp1/ +MEDIA DRIVERS FOR HELENE +M: Abylay Ospan <aospan@netup.ru> +L: linux-media@vger.kernel.org +W: https://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +S: Supported +F: drivers/media/dvb-frontends/helene* + MEDIA DRIVERS FOR ASCOT2E M: Sergey Kozlov <serjk@netup.ru> +M: Abylay Ospan <aospan@netup.ru> L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7377,6 +7397,7 @@ F: drivers/media/dvb-frontends/ascot2e* MEDIA DRIVERS FOR CXD2841ER M: Sergey Kozlov <serjk@netup.ru> +M: Abylay Ospan <aospan@netup.ru> L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7386,6 +7407,7 @@ F: drivers/media/dvb-frontends/cxd2841er* MEDIA DRIVERS FOR HORUS3A M: Sergey Kozlov <serjk@netup.ru> +M: Abylay Ospan <aospan@netup.ru> L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7395,6 +7417,7 @@ F: drivers/media/dvb-frontends/horus3a* MEDIA DRIVERS FOR LNBH25 M: Sergey Kozlov <serjk@netup.ru> +M: Abylay Ospan <aospan@netup.ru> L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7404,6 +7427,7 @@ F: drivers/media/dvb-frontends/lnbh25* MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices M: Sergey Kozlov <serjk@netup.ru> +M: Abylay Ospan <aospan@netup.ru> L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7653,10 +7677,8 @@ L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://palosaari.fi/linux/ Q: http://patchwork.linuxtv.org/project/linux-media/list/ -T: git git://linuxtv.org/anttip/media_tree.git S: Maintained -F: drivers/staging/media/mn88472/ -F: drivers/media/dvb-frontends/mn88472.h +F: drivers/media/dvb-frontends/mn88472* MN88473 MEDIA DRIVER M: Antti Palosaari <crope@iki.fi> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c index e671a7cd3463..6ac717f2056f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c @@ -148,40 +148,39 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane) struct rcar_du_vsp_plane_state *state = to_rcar_vsp_plane_state(plane->plane.state); struct drm_framebuffer *fb = plane->plane.state->fb; - struct v4l2_rect src; - struct v4l2_rect dst; - dma_addr_t paddr[2] = { 0, }; - u32 pixelformat = 0; + struct vsp1_du_atomic_config cfg = { + .pixelformat = 0, + .pitch = fb->pitches[0], + .alpha = state->alpha, + .zpos = state->zpos, + }; unsigned int i; - src.left = state->state.src_x >> 16; - src.top = state->state.src_y >> 16; - src.width = state->state.src_w >> 16; - src.height = state->state.src_h >> 16; + cfg.src.left = state->state.src_x >> 16; + cfg.src.top = state->state.src_y >> 16; + cfg.src.width = state->state.src_w >> 16; + cfg.src.height = state->state.src_h >> 16; - dst.left = state->state.crtc_x; - dst.top = state->state.crtc_y; - dst.width = state->state.crtc_w; - dst.height = state->state.crtc_h; + cfg.dst.left = state->state.crtc_x; + cfg.dst.top = state->state.crtc_y; + cfg.dst.width = state->state.crtc_w; + cfg.dst.height = state->state.crtc_h; for (i = 0; i < state->format->planes; ++i) { struct drm_gem_cma_object *gem; gem = drm_fb_cma_get_gem_obj(fb, i); - paddr[i] = gem->paddr + fb->offsets[i]; + cfg.mem[i] = gem->paddr + fb->offsets[i]; } for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) { if (formats_kms[i] == state->format->fourcc) { - pixelformat = formats_v4l2[i]; + cfg.pixelformat = formats_v4l2[i]; break; } } - WARN_ON(!pixelformat); - - vsp1_du_atomic_update(plane->vsp->vsp, plane->index, pixelformat, - fb->pitches[0], paddr, &src, &dst); + vsp1_du_atomic_update(plane->vsp->vsp, plane->index, &cfg); } static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane, @@ -220,8 +219,7 @@ static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane, if (plane->state->crtc) rcar_du_vsp_plane_setup(rplane); else - vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, 0, 0, 0, - NULL, NULL); + vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, NULL); } static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = { @@ -269,6 +267,7 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane) return; state->alpha = 255; + state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; plane->state = &state->state; plane->state->plane = plane; @@ -283,6 +282,8 @@ static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane, if (property == rcdu->props.alpha) rstate->alpha = val; + else if (property == rcdu->props.zpos) + rstate->zpos = val; else return -EINVAL; @@ -299,6 +300,8 @@ static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane, if (property == rcdu->props.alpha) *val = rstate->alpha; + else if (property == rcdu->props.zpos) + *val = rstate->zpos; else return -EINVAL; @@ -378,6 +381,8 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp) drm_object_attach_property(&plane->plane.base, rcdu->props.alpha, 255); + drm_object_attach_property(&plane->plane.base, + rcdu->props.zpos, 1); } return 0; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h index df3bf3805c69..510dcc9c6816 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h @@ -44,6 +44,7 @@ static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p) * @state: base DRM plane state * @format: information about the pixel format used by the plane * @alpha: value of the plane alpha property + * @zpos: value of the plane zpos property */ struct rcar_du_vsp_plane_state { struct drm_plane_state state; @@ -51,6 +52,7 @@ struct rcar_du_vsp_plane_state { const struct rcar_du_format_info *format; unsigned int alpha; + unsigned int zpos; }; static inline struct rcar_du_vsp_plane_state * diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index f82cd1ff4f3a..b1e3a26b1431 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -161,6 +161,18 @@ struct dvb_ca_private { struct mutex ioctl_mutex; }; +static void dvb_ca_private_free(struct dvb_ca_private *ca) +{ + unsigned int i; + + dvb_unregister_device(ca->dvbdev); + for (i = 0; i < ca->slot_count; i++) + vfree(ca->slot_info[i].rx_buffer.data); + + kfree(ca->slot_info); + kfree(ca); +} + static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); @@ -1759,10 +1771,7 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca) for (i = 0; i < ca->slot_count; i++) { dvb_ca_en50221_slot_shutdown(ca, i); - vfree(ca->slot_info[i].rx_buffer.data); } - kfree(ca->slot_info); - dvb_unregister_device(ca->dvbdev); - kfree(ca); + dvb_ca_private_free(ca); pubca->private = NULL; } diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index a82f77c49bd5..c645aa81f423 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -73,6 +73,14 @@ config DVB_SI2165 Say Y when you want to support this frontend. +config DVB_MN88472 + tristate "Panasonic MN88472" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + config DVB_MN88473 tristate "Panasonic MN88473" depends on DVB_CORE && I2C @@ -853,6 +861,13 @@ config DVB_ASCOT2E help Say Y when you want to support this frontend. +config DVB_HELENE + tristate "Sony HELENE Sat/Ter tuner (CXD2858ER)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index eb7191f4219d..e90165ad361b 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o obj-$(CONFIG_DVB_STV090x) += stv090x.o obj-$(CONFIG_DVB_STV6110x) += stv6110x.o obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o +obj-$(CONFIG_DVB_MN88472) += mn88472.o obj-$(CONFIG_DVB_MN88473) += mn88473.o obj-$(CONFIG_DVB_ISL6423) += isl6423.o obj-$(CONFIG_DVB_EC100) += ec100.o @@ -123,3 +124,4 @@ obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o obj-$(CONFIG_DVB_HORUS3A) += horus3a.o obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o +obj-$(CONFIG_DVB_HELENE) += helene.o diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 900186ba8e62..d369a7567d18 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -1,7 +1,9 @@ /* * cxd2841er.c * - * Sony CXD2441ER digital demodulator driver + * Sony digital demodulator driver for + * CXD2841ER - DVB-S/S2/T/T2/C/C2 + * CXD2854ER - DVB-S/S2/T/T2/C/C2, ISDB-T/S * * Copyright 2012 Sony Corporation * Copyright (C) 2014 NetUP Inc. @@ -51,6 +53,8 @@ struct cxd2841er_priv { const struct cxd2841er_config *config; enum cxd2841er_state state; u8 system; + enum cxd2841er_xtal xtal; + enum fe_caps caps; }; static const struct cxd2841er_cnr_data s_cn_data[] = { @@ -188,6 +192,9 @@ static const struct cxd2841er_cnr_data s2_cn_data[] = { }; #define MAKE_IFFREQ_CONFIG(iffreq) ((u32)(((iffreq)/41.0)*16777216.0 + 0.5)) +#define MAKE_IFFREQ_CONFIG_XTAL(xtal, iffreq) ((xtal == SONY_XTAL_24000) ? \ + (u32)(((iffreq)/48.0)*16777216.0 + 0.5) : \ + (u32)(((iffreq)/41.0)*16777216.0 + 0.5)) static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv, u8 addr, u8 reg, u8 write, @@ -217,7 +224,7 @@ static int cxd2841er_write_regs(struct cxd2841er_priv *priv, }; if (len + 1 >= sizeof(buf)) { - dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + dev_warn(&priv->i2c->dev, "wr reg=%04x: len=%d is too big!\n", reg, len + 1); return -E2BIG; } @@ -282,6 +289,7 @@ static int cxd2841er_read_regs(struct cxd2841er_priv *priv, KBUILD_MODNAME, ret, i2c_addr, reg); return ret; } + cxd2841er_i2c_debug(priv, i2c_addr, reg, 0, val, len); return 0; } @@ -427,6 +435,15 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, u32 bandwidth); +static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv); + +static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv); + +static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv); + static int cxd2841er_retune_active(struct cxd2841er_priv *priv, struct dtv_frontend_properties *p) { @@ -454,7 +471,13 @@ static int cxd2841er_retune_active(struct cxd2841er_priv *priv, priv, p->bandwidth_hz); case SYS_DVBC_ANNEX_A: return cxd2841er_sleep_tc_to_active_c_band( - priv, 8000000); + priv, p->bandwidth_hz); + case SYS_ISDBT: + cxd2841er_active_i_to_sleep_tc(priv); + cxd2841er_sleep_tc_to_shutdown(priv); + cxd2841er_shutdown_to_sleep_tc(priv); + return cxd2841er_sleep_tc_to_active_i( + priv, p->bandwidth_hz); } } dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", @@ -669,6 +692,45 @@ static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv) return 0; } +static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + + /* TODO: Cancel demod parameter */ + + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) { dev_dbg(&priv->i2c->dev, "%s()\n", __func__); @@ -686,8 +748,25 @@ static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); /* Set demod SW reset */ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); - /* Set X'tal clock to 20.5Mhz */ - cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + + switch (priv->xtal) { + case SONY_XTAL_20500: + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + break; + case SONY_XTAL_24000: + /* Select demod frequency */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x03); + break; + case SONY_XTAL_41000: + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x01); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid demod xtal %d\n", + __func__, priv->xtal); + return -EINVAL; + } + /* Set demod mode */ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a); /* Clear demod SW reset */ @@ -712,6 +791,8 @@ static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) { + u8 data = 0; + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); if (priv->state != STATE_SHUTDOWN) { dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", @@ -727,9 +808,24 @@ static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); /* Set demod SW reset */ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); - /* Set X'tal clock to 20.5Mhz */ + /* Select ADC clock mode */ cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00); - cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + + switch (priv->xtal) { + case SONY_XTAL_20500: + data = 0x0; + break; + case SONY_XTAL_24000: + /* Select demod frequency */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + data = 0x3; + break; + case SONY_XTAL_41000: + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + data = 0x1; + break; + } + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, data); /* Clear demod SW reset */ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00); usleep_range(1000, 2000); @@ -809,11 +905,14 @@ static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv, static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv) { - u8 chip_id; + u8 chip_id = 0; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); - cxd2841er_write_reg(priv, I2C_SLVT, 0, 0); - cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id); + if (cxd2841er_write_reg(priv, I2C_SLVT, 0, 0) == 0) + cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id); + else if (cxd2841er_write_reg(priv, I2C_SLVX, 0, 0) == 0) + cxd2841er_read_reg(priv, I2C_SLVX, 0xfd, &chip_id); + return chip_id; } @@ -896,6 +995,25 @@ static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock) return 0; } +static int cxd2841er_read_status_i(struct cxd2841er_priv *priv, + u8 *sync, u8 *tslock, u8 *unlock) +{ + u8 data = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + /* Set SLV-T Bank : 0x60 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + dev_dbg(&priv->i2c->dev, + "%s(): lock=0x%x\n", __func__, data); + *sync = ((data & 0x02) ? 1 : 0); + *tslock = ((data & 0x01) ? 1 : 0); + *unlock = ((data & 0x10) ? 1 : 0); + return 0; +} + static int cxd2841er_read_status_tc(struct dvb_frontend *fe, enum fe_status *status) { @@ -921,6 +1039,20 @@ static int cxd2841er_read_status_tc(struct dvb_frontend *fe, FE_HAS_SYNC; if (tslock) *status |= FE_HAS_LOCK; + } else if (priv->system == SYS_ISDBT) { + ret = cxd2841er_read_status_i( + priv, &sync, &tslock, &unlock); + if (ret) + goto done; + if (unlock) + goto done; + if (sync) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC; + if (tslock) + *status |= FE_HAS_LOCK; } else if (priv->system == SYS_DVBC_ANNEX_A) { ret = cxd2841er_read_status_c(priv, &tslock); if (ret) @@ -997,6 +1129,76 @@ static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv, return 0; } +static int cxd2841er_get_carrier_offset_i(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_ISDBT) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 29); + + switch (bandwidth) { + case 6000000: + *offset = -1 * ((*offset) * 8/264); + break; + case 7000000: + *offset = -1 * ((*offset) * 8/231); + break; + case 8000000: + *offset = -1 * ((*offset) * 8/198); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + + dev_dbg(&priv->i2c->dev, "%s(): bandwidth %d offset %d\n", + __func__, bandwidth, *offset); + + return 0; +} + +static int cxd2841er_get_carrier_offset_t(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBT) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 29); + *offset *= (bandwidth / 1000000); + *offset /= 235; + return 0; +} + static int cxd2841er_get_carrier_offset_t2(struct cxd2841er_priv *priv, u32 bandwidth, int *offset) { @@ -1096,6 +1298,38 @@ static int cxd2841er_read_packet_errors_t2( return 0; } +static int cxd2841er_read_packet_errors_i( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[2]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0xA1, data, 1); + + if (!(data[0] & 0x01)) + return 0; + + /* Layer A */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA2, data, sizeof(data)); + *penum = ((u32)data[0] << 8) | (u32)data[1]; + + /* Layer B */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA4, data, sizeof(data)); + *penum += ((u32)data[0] << 8) | (u32)data[1]; + + /* Layer C */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA6, data, sizeof(data)); + *penum += ((u32)data[0] << 8) | (u32)data[1]; + + return 0; +} + static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv) { u8 data[11]; @@ -1391,6 +1625,37 @@ static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) return 0; } +static int cxd2841er_read_snr_i(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, + priv->state); + return -EINVAL; + } + + /* Freeze all registers */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01); + + + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + if (reg > 4996) + reg = 4996; + *snr = 100 * intlog10(reg) - 9031; + return 0; +} + static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, u8 delsys) { @@ -1399,6 +1664,26 @@ static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, cxd2841er_write_reg( priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20)); cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); + return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; +} + +static u16 cxd2841er_read_agc_gain_i(struct cxd2841er_priv *priv, + u8 delsys) +{ + u8 data[2]; + + cxd2841er_write_reg( + priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; } @@ -1455,6 +1740,10 @@ static int cxd2841er_read_signal_strength(struct dvb_frontend *fe, *strength = 65535 - cxd2841er_read_agc_gain_t_t2( priv, p->delivery_system); break; + case SYS_ISDBT: + *strength = 65535 - cxd2841er_read_agc_gain_i( + priv, p->delivery_system); + break; case SYS_DVBS: case SYS_DVBS2: *strength = 65535 - cxd2841er_read_agc_gain_s(priv); @@ -1480,6 +1769,9 @@ static int cxd2841er_read_snr(struct dvb_frontend *fe, u16 *snr) case SYS_DVBT2: cxd2841er_read_snr_t2(priv, &tmp); break; + case SYS_ISDBT: + cxd2841er_read_snr_i(priv, &tmp); + break; case SYS_DVBS: case SYS_DVBS2: tmp = cxd2841er_dvbs_read_snr(priv, p->delivery_system); @@ -1506,6 +1798,9 @@ static int cxd2841er_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) case SYS_DVBT2: cxd2841er_read_packet_errors_t2(priv, ucblocks); break; + case SYS_ISDBT: + cxd2841er_read_packet_errors_i(priv, ucblocks); + break; default: *ucblocks = 0; break; @@ -1524,15 +1819,18 @@ static int cxd2841er_dvbt2_set_profile( switch (profile) { case DVBT2_PROFILE_BASE: tune_mode = 0x01; - seq_not2d_time = 12; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x0E:0x0C; break; case DVBT2_PROFILE_LITE: tune_mode = 0x05; - seq_not2d_time = 40; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28; break; case DVBT2_PROFILE_ANY: tune_mode = 0x00; - seq_not2d_time = 40; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28; break; default: return -EINVAL; @@ -1574,254 +1872,617 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, u32 bandwidth) { u32 iffreq; - u8 b20_9f[5]; - u8 b10_a6[14]; - u8 b10_b6[3]; - u8 b10_d7; + u8 data[MAX_WRITE_REGSIZE]; + + const uint8_t nominalRate8bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + const uint8_t nominalRate7bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + const uint8_t nominalRate6bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */ + {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */ + }; + + const uint8_t nominalRate5bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */ + {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */ + {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */ + }; + + const uint8_t nominalRate17bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x58, 0xE2, 0xAF, 0xE0, 0xBC}, /* 20.5MHz XTal */ + {0x68, 0x0F, 0xA2, 0x32, 0xD0}, /* 24MHz XTal */ + {0x58, 0xE2, 0xAF, 0xE0, 0xBC} /* 41MHz XTal */ + }; + + const uint8_t itbCoef8bw[3][14] = { + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, + 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, + 0x29, 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */ + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, + 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */ + }; + + const uint8_t itbCoef7bw[3][14] = { + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, + 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, + 0x29, 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */ + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, + 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */ + }; + + const uint8_t itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, + 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + const uint8_t itbCoef5bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, + 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + const uint8_t itbCoef17bw[3][14] = { + {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B, + 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99}, /* 20.5MHz XTal */ + {0x33, 0x8E, 0x2B, 0x97, 0x2D, 0x95, 0x37, 0x8B, + 0x30, 0x97, 0x2D, 0x9A, 0x21, 0xA4}, /* 24MHz XTal */ + {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B, + 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99} /* 41MHz XTal */ + }; + + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (bandwidth) { case 8000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x11; - b20_9f[1] = 0xf0; - b20_9f[2] = 0x00; - b20_9f[3] = 0x00; - b20_9f[4] = 0x00; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x26; - b10_a6[1] = 0xaf; - b10_a6[2] = 0x06; - b10_a6[3] = 0xcd; - b10_a6[4] = 0x13; - b10_a6[5] = 0xbb; - b10_a6[6] = 0x28; - b10_a6[7] = 0xba; - b10_a6[8] = 0x23; - b10_a6[9] = 0xa9; - b10_a6[10] = 0x1f; - b10_a6[11] = 0xa8; - b10_a6[12] = 0x2c; - b10_a6[13] = 0xc8; - iffreq = MAKE_IFFREQ_CONFIG(4.80); - b10_d7 = 0x00; + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + /* <IF freq setting> */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x00, 0x07); break; case 7000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x14; - b20_9f[1] = 0x80; - b20_9f[2] = 0x00; - b20_9f[3] = 0x00; - b20_9f[4] = 0x00; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x2C; - b10_a6[1] = 0xBD; - b10_a6[2] = 0x02; - b10_a6[3] = 0xCF; - b10_a6[4] = 0x04; - b10_a6[5] = 0xF8; - b10_a6[6] = 0x23; - b10_a6[7] = 0xA6; - b10_a6[8] = 0x29; - b10_a6[9] = 0xB0; - b10_a6[10] = 0x26; - b10_a6[11] = 0xA9; - b10_a6[12] = 0x21; - b10_a6[13] = 0xA5; - iffreq = MAKE_IFFREQ_CONFIG(4.2); - b10_d7 = 0x02; + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + /* <IF freq setting> */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x02, 0x07); break; case 6000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x17; - b20_9f[1] = 0xEA; - b20_9f[2] = 0xAA; - b20_9f[3] = 0xAA; - b20_9f[4] = 0xAA; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x27; - b10_a6[1] = 0xA7; - b10_a6[2] = 0x28; - b10_a6[3] = 0xB3; - b10_a6[4] = 0x02; - b10_a6[5] = 0xF0; - b10_a6[6] = 0x01; - b10_a6[7] = 0xE8; - b10_a6[8] = 0x00; - b10_a6[9] = 0xCF; - b10_a6[10] = 0x00; - b10_a6[11] = 0xE6; - b10_a6[12] = 0x23; - b10_a6[13] = 0xA4; - iffreq = MAKE_IFFREQ_CONFIG(3.6); - b10_d7 = 0x04; + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + /* <IF freq setting> */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x04, 0x07); break; case 5000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x1C; - b20_9f[1] = 0xB3; - b20_9f[2] = 0x33; - b20_9f[3] = 0x33; - b20_9f[4] = 0x33; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x27; - b10_a6[1] = 0xA7; - b10_a6[2] = 0x28; - b10_a6[3] = 0xB3; - b10_a6[4] = 0x02; - b10_a6[5] = 0xF0; - b10_a6[6] = 0x01; - b10_a6[7] = 0xE8; - b10_a6[8] = 0x00; - b10_a6[9] = 0xCF; - b10_a6[10] = 0x00; - b10_a6[11] = 0xE6; - b10_a6[12] = 0x23; - b10_a6[13] = 0xA4; - iffreq = MAKE_IFFREQ_CONFIG(3.6); - b10_d7 = 0x06; + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate5bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef5bw[priv->xtal], 14); + /* <IF freq setting> */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x06, 0x07); break; case 1712000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x58; - b20_9f[1] = 0xE2; - b20_9f[2] = 0xAF; - b20_9f[3] = 0xE0; - b20_9f[4] = 0xBC; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x25; - b10_a6[1] = 0xA0; - b10_a6[2] = 0x36; - b10_a6[3] = 0x8D; - b10_a6[4] = 0x2E; - b10_a6[5] = 0x94; - b10_a6[6] = 0x28; - b10_a6[7] = 0x9B; - b10_a6[8] = 0x32; - b10_a6[9] = 0x90; - b10_a6[10] = 0x2C; - b10_a6[11] = 0x9D; - b10_a6[12] = 0x29; - b10_a6[13] = 0x99; - iffreq = MAKE_IFFREQ_CONFIG(3.5); - b10_d7 = 0x03; + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate17bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x03, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef17bw[priv->xtal], 14); + /* <IF freq setting> */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.50); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x03, 0x07); break; default: return -EINVAL; } - /* Set SLV-T Bank : 0x20 */ - cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x20); - cxd2841er_write_regs(priv, I2C_SLVT, 0x9f, b20_9f, sizeof(b20_9f)); - /* Set SLV-T Bank : 0x27 */ - cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); - cxd2841er_set_reg_bits( - priv, I2C_SLVT, 0x7a, - (bandwidth == 1712000 ? 0x03 : 0x00), 0x0f); - /* Set SLV-T Bank : 0x10 */ - cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); - /* Group delay equaliser sett. for ASCOT2E */ - cxd2841er_write_regs(priv, I2C_SLVT, 0xa6, b10_a6, sizeof(b10_a6)); - /* <IF freq setting> */ - b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); - b10_b6[1] = (u8)((iffreq >> 8) & 0xff); - b10_b6[2] = (u8)(iffreq & 0xff); - cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); - /* System bandwidth setting */ - cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, b10_d7, 0x07); return 0; } static int cxd2841er_sleep_tc_to_active_t_band( struct cxd2841er_priv *priv, u32 bandwidth) { - u8 b13_9c[2] = { 0x01, 0x14 }; - u8 bw8mhz_b10_9f[] = { 0x11, 0xF0, 0x00, 0x00, 0x00 }; - u8 bw8mhz_b10_a6[] = { 0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, - 0x28, 0xBA, 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8 }; - u8 bw8mhz_b10_d9[] = { 0x01, 0xE0 }; - u8 bw8mhz_b17_38[] = { 0x01, 0x02 }; - u8 bw7mhz_b10_9f[] = { 0x14, 0x80, 0x00, 0x00, 0x00 }; - u8 bw7mhz_b10_a6[] = { 0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, - 0x23, 0xA6, 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5 }; - u8 bw7mhz_b10_d9[] = { 0x12, 0xF8 }; - u8 bw7mhz_b17_38[] = { 0x00, 0x03 }; - u8 bw6mhz_b10_9f[] = { 0x17, 0xEA, 0xAA, 0xAA, 0xAA }; - u8 bw6mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, - 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; - u8 bw6mhz_b10_d9[] = { 0x1F, 0xDC }; - u8 bw6mhz_b17_38[] = { 0x00, 0x03 }; - u8 bw5mhz_b10_9f[] = { 0x1C, 0xB3, 0x33, 0x33, 0x33 }; - u8 bw5mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, - 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; - u8 bw5mhz_b10_d9[] = { 0x26, 0x3C }; - u8 bw5mhz_b17_38[] = { 0x00, 0x03 }; - u8 b10_b6[3]; - u8 d7val; + u8 data[MAX_WRITE_REGSIZE]; u32 iffreq; - u8 *b10_9f; - u8 *b10_a6; - u8 *b10_d9; - u8 *b17_38; + u8 nominalRate8bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + u8 nominalRate7bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + u8 nominalRate6bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */ + {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */ + }; + u8 nominalRate5bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */ + {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */ + {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */ + }; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + u8 itbCoef8bw[3][14] = { + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9, + 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, 0xA5, + 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */ + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9, + 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */ + }; + u8 itbCoef7bw[3][14] = { + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0, + 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, 0xA2, + 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */ + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0, + 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */ + }; + u8 itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4, + 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + u8 itbCoef5bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4, + 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + /* Set SLV-T Bank : 0x13 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); /* Echo performance optimization setting */ - cxd2841er_write_regs(priv, I2C_SLVT, 0x9c, b13_9c, sizeof(b13_9c)); + data[0] = 0x01; + data[1] = 0x14; + cxd2841er_write_regs(priv, I2C_SLVT, 0x9C, data, 2); + + /* Set SLV-T Bank : 0x10 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); switch (bandwidth) { case 8000000: - b10_9f = bw8mhz_b10_9f; - b10_a6 = bw8mhz_b10_a6; - b10_d9 = bw8mhz_b10_d9; - b17_38 = bw8mhz_b17_38; - d7val = 0; - iffreq = MAKE_IFFREQ_CONFIG(4.80); + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + /* <IF freq setting> */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x00, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x15; + data[1] = 0x28; + } else { + data[0] = 0x01; + data[1] = 0xE0; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x01; + data[1] = 0x02; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); break; case 7000000: - b10_9f = bw7mhz_b10_9f; - b10_a6 = bw7mhz_b10_a6; - b10_d9 = bw7mhz_b10_d9; - b17_38 = bw7mhz_b17_38; - d7val = 2; - iffreq = MAKE_IFFREQ_CONFIG(4.20); + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + /* <IF freq setting> */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x02, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x1F; + data[1] = 0xF8; + } else { + data[0] = 0x12; + data[1] = 0xF8; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); break; case 6000000: - b10_9f = bw6mhz_b10_9f; - b10_a6 = bw6mhz_b10_a6; - b10_d9 = bw6mhz_b10_d9; - b17_38 = bw6mhz_b17_38; - d7val = 4; - iffreq = MAKE_IFFREQ_CONFIG(3.60); + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + /* <IF freq setting> */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x04, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x25; + data[1] = 0x4C; + } else { + data[0] = 0x1F; + data[1] = 0xDC; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); break; case 5000000: - b10_9f = bw5mhz_b10_9f; - b10_a6 = bw5mhz_b10_a6; - b10_d9 = bw5mhz_b10_d9; - b17_38 = bw5mhz_b17_38; - d7val = 6; - iffreq = MAKE_IFFREQ_CONFIG(3.60); + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate5bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef5bw[priv->xtal], 14); + /* <IF freq setting> */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x06, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x2C; + data[1] = 0xC2; + } else { + data[0] = 0x26; + data[1] = 0x3C; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); + break; + } + + return 0; +} + +static int cxd2841er_sleep_tc_to_active_i_band( + struct cxd2841er_priv *priv, u32 bandwidth) +{ + u32 iffreq; + u8 data[3]; + + /* TRCG Nominal Rate */ + u8 nominalRate8bw[3][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x11, 0xB8, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + u8 nominalRate7bw[3][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x14, 0x40, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + u8 nominalRate6bw[3][5] = { + {0x14, 0x2E, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x17, 0xA0, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x2E, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + u8 itbCoef8bw[3][14] = { + {0x00}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, + 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz Xtal */ + {0x0}, /* 41MHz XTal */ + }; + + u8 itbCoef7bw[3][14] = { + {0x00}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, + 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz Xtal */ + {0x00}, /* 41MHz XTal */ + }; + + u8 itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, + 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, + 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz Xtal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, + 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 41MHz XTal */ + }; + + dev_dbg(&priv->i2c->dev, "%s() bandwidth=%u\n", __func__, bandwidth); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* 20.5/41MHz Xtal support is not available + * on ISDB-T 7MHzBW and 8MHzBW + */ + if (priv->xtal != SONY_XTAL_24000 && bandwidth > 6000000) { + dev_err(&priv->i2c->dev, + "%s(): bandwidth %d supported only for 24MHz xtal\n", + __func__, bandwidth); + return -EINVAL; + } + + switch (bandwidth) { + case 8000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + + /* IF freq setting */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.75); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x0, 0x7); + + /* Demod core latency setting */ + data[0] = 0x13; + data[1] = 0xFC; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x03); + break; + case 7000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + + /* IF freq setting */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.15); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x02, 0x7); + + /* Demod core latency setting */ + data[0] = 0x1A; + data[1] = 0xFA; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02); + break; + case 6000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + + /* IF freq setting */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.55); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x04, 0x7); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x1F; + data[1] = 0x79; + } else { + data[0] = 0x1A; + data[1] = 0xE2; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x07, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02); break; default: dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", - __func__, bandwidth); + __func__, bandwidth); return -EINVAL; } - /* <IF freq setting> */ - b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); - b10_b6[1] = (u8)((iffreq >> 8) & 0xff); - b10_b6[2] = (u8)(iffreq & 0xff); - cxd2841er_write_regs( - priv, I2C_SLVT, 0x9f, b10_9f, sizeof(bw8mhz_b10_9f)); - cxd2841er_write_regs( - priv, I2C_SLVT, 0xa6, b10_a6, sizeof(bw8mhz_b10_a6)); - cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); - cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, d7val, 0x7); - cxd2841er_write_regs( - priv, I2C_SLVT, 0xd9, b10_d9, sizeof(bw8mhz_b10_d9)); - cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); - cxd2841er_write_regs( - priv, I2C_SLVT, 0x38, b17_38, sizeof(bw8mhz_b17_38)); return 0; } @@ -1837,7 +2498,7 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, u8 b10_b6[3]; u32 iffreq; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() bw=%d\n", __func__, bandwidth); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); switch (bandwidth) { case 8000000: @@ -1854,7 +2515,7 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, iffreq = MAKE_IFFREQ_CONFIG(3.7); break; default: - dev_dbg(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n", + dev_err(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n", __func__, bandwidth); return -EINVAL; } @@ -1902,6 +2563,7 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, u32 bandwidth) { u8 data[2] = { 0x09, 0x54 }; + u8 data24m[3] = {0xDC, 0x6C, 0x00}; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); @@ -1919,7 +2581,11 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); /* Enable ADC 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); - /* xtal freq 20.5MHz */ + /* Enable ADC 2 & 3 */ + if (priv->xtal == SONY_XTAL_41000) { + data[0] = 0x0A; + data[1] = 0xD4; + } cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); /* Enable ADC 4 */ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); @@ -1947,6 +2613,15 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, /* TSIF setting */ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + + if (priv->xtal == SONY_XTAL_24000) { + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBF, 0x60); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18); + cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data24m, 3); + } + cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth); /* Set SLV-T Bank : 0x00 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); @@ -1961,7 +2636,7 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, u32 bandwidth) { - u8 data[2] = { 0x09, 0x54 }; + u8 data[MAX_WRITE_REGSIZE]; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2); @@ -1974,12 +2649,21 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, /* Enable demod clock */ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x00); cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); /* Enable ADC clock */ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); /* Enable ADC 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); - /* xtal freq 20.5MHz */ + + if (priv->xtal == SONY_XTAL_41000) { + data[0] = 0x0A; + data[1] = 0xD4; + } else { + data[0] = 0x09; + data[1] = 0x54; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); /* Enable ADC 4 */ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); @@ -2002,6 +2686,10 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, /* Set SLV-T Bank : 0x2b */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70); + /* Set SLV-T Bank : 0x23 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23); + /* L1 Control setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE6, 0x00, 0x03); /* Set SLV-T Bank : 0x00 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); /* TSIF setting */ @@ -2020,6 +2708,72 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f); + /* 24MHz Xtal setting */ + if (priv->xtal == SONY_XTAL_24000) { + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + data[0] = 0xEB; + data[1] = 0x03; + data[2] = 0x3B; + cxd2841er_write_regs(priv, I2C_SLVT, 0x33, data, 3); + + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + data[0] = 0x5E; + data[1] = 0x5E; + data[2] = 0x47; + cxd2841er_write_regs(priv, I2C_SLVT, 0x95, data, 3); + + cxd2841er_write_reg(priv, I2C_SLVT, 0x99, 0x18); + + data[0] = 0x3F; + data[1] = 0xFF; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Set SLV-T Bank : 0x24 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24); + data[0] = 0x0B; + data[1] = 0x72; + cxd2841er_write_regs(priv, I2C_SLVT, 0x34, data, 2); + + data[0] = 0x93; + data[1] = 0xF3; + data[2] = 0x00; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD2, data, 3); + + data[0] = 0x05; + data[1] = 0xB8; + data[2] = 0xD8; + cxd2841er_write_regs(priv, I2C_SLVT, 0xDD, data, 3); + + cxd2841er_write_reg(priv, I2C_SLVT, 0xE0, 0x00); + + /* Set SLV-T Bank : 0x25 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x25); + cxd2841er_write_reg(priv, I2C_SLVT, 0xED, 0x60); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0xFA, 0x34); + + /* Set SLV-T Bank : 0x2B */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2B); + cxd2841er_write_reg(priv, I2C_SLVT, 0x4B, 0x2F); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9E, 0x0E); + + /* Set SLV-T Bank : 0x2D */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2D); + data[0] = 0x89; + data[1] = 0x89; + cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data, 2); + + /* Set SLV-T Bank : 0x5E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x5E); + data[0] = 0x24; + data[1] = 0x95; + cxd2841er_write_regs(priv, I2C_SLVT, 0x8C, data, 2); + } + cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth); /* Set SLV-T Bank : 0x00 */ @@ -2032,6 +2786,84 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, return 0; } +/* ISDB-Tb part */ +static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + u8 data24m[2] = {0x60, 0x00}; + u8 data24m2[3] = {0xB7, 0x1B, 0x00}; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x06); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Enable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x01); + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x01); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz or 24M */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* ASCOT setting ON */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01); + /* FEC Auto Recovery setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x00, 0x01); + /* ISDB-T initial setting */ + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x00, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x00, 0x01); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x69, 0x04, 0x07); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x6B, 0x03, 0x07); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9D, 0x50, 0xFF); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xD3, 0x06, 0x1F); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xED, 0x00, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE2, 0xCE, 0x80); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xF2, 0x13, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x2E, 0x3F); + /* Set SLV-T Bank : 0x15 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x02, 0x03); + /* Set SLV-T Bank : 0x1E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x1E); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x73, 0x68, 0xFF); + /* Set SLV-T Bank : 0x63 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x63); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x81, 0x00, 0x01); + + /* for xtal 24MHz */ + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_write_regs(priv, I2C_SLVT, 0xBF, data24m, 2); + /* Set SLV-T Bank : 0x60 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_write_regs(priv, I2C_SLVT, 0xA8, data24m2, 3); + + cxd2841er_sleep_tc_to_active_i_band(priv, bandwidth); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, u32 bandwidth) { @@ -2079,7 +2911,7 @@ static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); - cxd2841er_sleep_tc_to_active_c_band(priv, 8000000); + cxd2841er_sleep_tc_to_active_c_band(priv, bandwidth); /* Set SLV-T Bank : 0x00 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); /* Disable HiZ Setting 1 */ @@ -2142,10 +2974,10 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) struct dtv_frontend_properties *p = &fe->dtv_property_cache; u32 symbol_rate = p->symbol_rate/1000; - dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d\n", + dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d xtal=%d\n", __func__, (p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"), - p->frequency, symbol_rate); + p->frequency, symbol_rate, priv->xtal); switch (priv->state) { case STATE_SLEEP_S: ret = cxd2841er_sleep_s_to_active_s( @@ -2199,7 +3031,8 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) struct cxd2841er_priv *priv = fe->demodulator_priv; struct dtv_frontend_properties *p = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() delivery_system=%d bandwidth_hz=%d\n", + __func__, p->delivery_system, p->bandwidth_hz); if (p->delivery_system == SYS_DVBT) { priv->system = SYS_DVBT; switch (priv->state) { @@ -2233,9 +3066,33 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) __func__, priv->state); ret = -EINVAL; } + } else if (p->delivery_system == SYS_ISDBT) { + priv->system = SYS_ISDBT; + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_i( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } } else if (p->delivery_system == SYS_DVBC_ANNEX_A || p->delivery_system == SYS_DVBC_ANNEX_C) { priv->system = SYS_DVBC_ANNEX_A; + /* correct bandwidth */ + if (p->bandwidth_hz != 6000000 && + p->bandwidth_hz != 7000000 && + p->bandwidth_hz != 8000000) { + p->bandwidth_hz = 8000000; + dev_dbg(&priv->i2c->dev, "%s(): forcing bandwidth to %d\n", + __func__, p->bandwidth_hz); + } + switch (priv->state) { case STATE_SLEEP_TC: ret = cxd2841er_sleep_tc_to_active_c( @@ -2321,7 +3178,8 @@ static int cxd2841er_tune_tc(struct dvb_frontend *fe, struct cxd2841er_priv *priv = fe->demodulator_priv; struct dtv_frontend_properties *p = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s(): re_tune %d\n", __func__, re_tune); + dev_dbg(&priv->i2c->dev, "%s(): re_tune %d bandwidth=%d\n", __func__, + re_tune, p->bandwidth_hz); if (re_tune) { ret = cxd2841er_set_frontend_tc(fe); if (ret) @@ -2329,7 +3187,16 @@ static int cxd2841er_tune_tc(struct dvb_frontend *fe, cxd2841er_read_status_tc(fe, status); if (*status & FE_HAS_LOCK) { switch (priv->system) { + case SYS_ISDBT: + ret = cxd2841er_get_carrier_offset_i( + priv, p->bandwidth_hz, + &carrier_offset); + break; case SYS_DVBT: + ret = cxd2841er_get_carrier_offset_t( + priv, p->bandwidth_hz, + &carrier_offset); + break; case SYS_DVBT2: ret = cxd2841er_get_carrier_offset_t2( priv, p->bandwidth_hz, @@ -2382,6 +3249,9 @@ static int cxd2841er_sleep_tc(struct dvb_frontend *fe) case SYS_DVBT2: cxd2841er_active_t2_to_sleep_tc(priv); break; + case SYS_ISDBT: + cxd2841er_active_i_to_sleep_tc(priv); + break; case SYS_DVBC_ANNEX_A: cxd2841er_active_c_to_sleep_tc(priv); break; @@ -2516,6 +3386,18 @@ static int cxd2841er_init_s(struct dvb_frontend *fe) { struct cxd2841er_priv *priv = fe->demodulator_priv; + /* sanity. force demod to SHUTDOWN state */ + if (priv->state == STATE_SLEEP_S) { + dev_dbg(&priv->i2c->dev, "%s() forcing sleep->shutdown\n", + __func__); + cxd2841er_sleep_s_to_shutdown(priv); + } else if (priv->state == STATE_ACTIVE_S) { + dev_dbg(&priv->i2c->dev, "%s() forcing active->sleep->shutdown\n", + __func__); + cxd2841er_active_s_to_sleep_s(priv); + cxd2841er_sleep_s_to_shutdown(priv); + } + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); cxd2841er_shutdown_to_sleep_s(priv); /* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */ @@ -2527,8 +3409,10 @@ static int cxd2841er_init_s(struct dvb_frontend *fe) static int cxd2841er_init_tc(struct dvb_frontend *fe) { struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() bandwidth_hz=%d\n", + __func__, p->bandwidth_hz); cxd2841er_shutdown_to_sleep_tc(priv); /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); @@ -2542,8 +3426,7 @@ static int cxd2841er_init_tc(struct dvb_frontend *fe) } static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops; -static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops; -static struct dvb_frontend_ops cxd2841er_dvbc_ops; +static struct dvb_frontend_ops cxd2841er_t_c_ops; static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, struct i2c_adapter *i2c, @@ -2551,6 +3434,7 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, { u8 chip_id = 0; const char *type; + const char *name; struct cxd2841er_priv *priv = NULL; /* allocate memory for the internal state */ @@ -2561,46 +3445,49 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, priv->config = cfg; priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1; priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1; - /* create dvb_frontend */ - switch (system) { - case SYS_DVBS: - memcpy(&priv->frontend.ops, - &cxd2841er_dvbs_s2_ops, - sizeof(struct dvb_frontend_ops)); - type = "S/S2"; - break; - case SYS_DVBT: - memcpy(&priv->frontend.ops, - &cxd2841er_dvbt_t2_ops, - sizeof(struct dvb_frontend_ops)); - type = "T/T2"; - break; - case SYS_DVBC_ANNEX_A: - memcpy(&priv->frontend.ops, - &cxd2841er_dvbc_ops, - sizeof(struct dvb_frontend_ops)); - type = "C/C2"; - break; - default: - kfree(priv); - return NULL; - } + priv->xtal = cfg->xtal; priv->frontend.demodulator_priv = priv; dev_info(&priv->i2c->dev, - "%s(): attaching CXD2841ER DVB-%s frontend\n", - __func__, type); - dev_info(&priv->i2c->dev, "%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n", __func__, priv->i2c, priv->i2c_addr_slvx, priv->i2c_addr_slvt); chip_id = cxd2841er_chip_id(priv); - if (chip_id != CXD2841ER_CHIP_ID) { + switch (chip_id) { + case CXD2841ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2841ER DVB-T/T2/C demodulator"); + name = "CXD2841ER"; + break; + case CXD2854ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2854ER DVB-T/T2/C and ISDB-T demodulator"); + cxd2841er_t_c_ops.delsys[3] = SYS_ISDBT; + name = "CXD2854ER"; + break; + default: dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n", - __func__, chip_id); + __func__, chip_id); priv->frontend.demodulator_priv = NULL; kfree(priv); return NULL; } + + /* create dvb_frontend */ + if (system == SYS_DVBS) { + memcpy(&priv->frontend.ops, + &cxd2841er_dvbs_s2_ops, + sizeof(struct dvb_frontend_ops)); + type = "S/S2"; + } else { + memcpy(&priv->frontend.ops, + &cxd2841er_t_c_ops, + sizeof(struct dvb_frontend_ops)); + type = "T/T2/C/ISDB-T"; + } + + dev_info(&priv->i2c->dev, + "%s(): attaching %s DVB-%s frontend\n", + __func__, name, type); dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n", __func__, chip_id); return &priv->frontend; @@ -2613,19 +3500,12 @@ struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, } EXPORT_SYMBOL(cxd2841er_attach_s); -struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, +struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - return cxd2841er_attach(cfg, i2c, SYS_DVBT); + return cxd2841er_attach(cfg, i2c, 0); } -EXPORT_SYMBOL(cxd2841er_attach_t); - -struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, - struct i2c_adapter *i2c) -{ - return cxd2841er_attach(cfg, i2c, SYS_DVBC_ANNEX_A); -} -EXPORT_SYMBOL(cxd2841er_attach_c); +EXPORT_SYMBOL(cxd2841er_attach_t_c); static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, @@ -2655,10 +3535,10 @@ static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { .tune = cxd2841er_tune_s }; -static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { - .delsys = { SYS_DVBT, SYS_DVBT2 }, +static struct dvb_frontend_ops cxd2841er_t_c_ops = { + .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, .info = { - .name = "Sony CXD2841ER DVB-T/T2 demodulator", + .name = "", /* will set in attach function */ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | @@ -2691,37 +3571,6 @@ static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { .get_frontend_algo = cxd2841er_get_algo }; -static struct dvb_frontend_ops cxd2841er_dvbc_ops = { - .delsys = { SYS_DVBC_ANNEX_A }, - .info = { - .name = "Sony CXD2841ER DVB-C demodulator", - .caps = FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 | - FE_CAN_QAM_AUTO | - FE_CAN_INVERSION_AUTO, - .frequency_min = 42000000, - .frequency_max = 1002000000 - }, - .init = cxd2841er_init_tc, - .sleep = cxd2841er_sleep_tc, - .release = cxd2841er_release, - .set_frontend = cxd2841er_set_frontend_tc, - .get_frontend = cxd2841er_get_frontend, - .read_status = cxd2841er_read_status_tc, - .tune = cxd2841er_tune_tc, - .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, - .get_frontend_algo = cxd2841er_get_algo, -}; - -MODULE_DESCRIPTION("Sony CXD2841ER DVB-C/C2/T/T2/S/S2 demodulator driver"); -MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>"); +MODULE_DESCRIPTION("Sony CXD2841ER/CXD2854ER DVB-C/C2/T/T2/S/S2 demodulator driver"); +MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>, Abylay Ospan <aospan@netup.ru>"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h index 3472bdd58949..62ad5f07390b 100644 --- a/drivers/media/dvb-frontends/cxd2841er.h +++ b/drivers/media/dvb-frontends/cxd2841er.h @@ -25,41 +25,39 @@ #include <linux/kconfig.h> #include <linux/dvb/frontend.h> +enum cxd2841er_xtal { + SONY_XTAL_20500, /* 20.5 MHz */ + SONY_XTAL_24000, /* 24 MHz */ + SONY_XTAL_41000 /* 41 MHz */ +}; + struct cxd2841er_config { u8 i2c_addr; + enum cxd2841er_xtal xtal; }; #if IS_REACHABLE(CONFIG_DVB_CXD2841ER) extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, struct i2c_adapter *i2c); -extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, - struct i2c_adapter *i2c); - -extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, +extern struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg, struct i2c_adapter *i2c); #else static inline struct dvb_frontend *cxd2841er_attach_s( struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } -static inline struct dvb_frontend *cxd2841er_attach_t( +static inline struct dvb_frontend *cxd2841er_attach_t_c( struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } -static inline struct dvb_frontend *cxd2841er_attach_c( - struct cxd2841er_config *cfg, struct i2c_adapter *i2c) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} #endif #endif diff --git a/drivers/media/dvb-frontends/cxd2841er_priv.h b/drivers/media/dvb-frontends/cxd2841er_priv.h index 33e2f495277b..0bbce451149f 100644 --- a/drivers/media/dvb-frontends/cxd2841er_priv.h +++ b/drivers/media/dvb-frontends/cxd2841er_priv.h @@ -26,6 +26,7 @@ #define I2C_SLVT 1 #define CXD2841ER_CHIP_ID 0xa7 +#define CXD2854ER_CHIP_ID 0xc1 #define CXD2841ER_DVBS_POLLING_INVL 10 diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index addffc33993a..447b518e287a 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -959,6 +959,15 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) /* enable ac coupling */ ds3000_writereg(state, 0x25, 0x8a); + if ((c->symbol_rate < ds3000_ops.info.symbol_rate_min) || + (c->symbol_rate > ds3000_ops.info.symbol_rate_max)) { + dprintk("%s() symbol_rate %u out of range (%u ... %u)\n", + __func__, c->symbol_rate, + ds3000_ops.info.symbol_rate_min, + ds3000_ops.info.symbol_rate_max); + return -EINVAL; + } + /* enhance symbol rate performance */ if ((c->symbol_rate / 1000) <= 5000) { value = 29777 / (c->symbol_rate / 1000) + 1; diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c new file mode 100644 index 000000000000..97a8982740a6 --- /dev/null +++ b/drivers/media/dvb-frontends/helene.c @@ -0,0 +1,1042 @@ +/* + * helene.c + * + * Sony HELENE DVB-S/S2 DVB-T/T2 DVB-C/C2 ISDB-T/S tuner driver (CXD2858ER) + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + * + * 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/slab.h> +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <linux/types.h> +#include "helene.h" +#include "dvb_frontend.h" + +#define MAX_WRITE_REGSIZE 20 + +enum helene_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct helene_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum helene_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); + enum helene_xtal xtal; +}; + +#define TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system) \ + (((tv_system) != SONY_HELENE_DTV_DVBC_6) && \ + ((tv_system) != SONY_HELENE_DTV_DVBC_8)\ + && ((tv_system) != SONY_HELENE_DTV_DVBC2_6) && \ + ((tv_system) != SONY_HELENE_DTV_DVBC2_8)) + +#define HELENE_AUTO 0xff +#define HELENE_OFFSET(ofs) ((u8)(ofs) & 0x1F) +#define HELENE_BW_6 0x00 +#define HELENE_BW_7 0x01 +#define HELENE_BW_8 0x02 +#define HELENE_BW_1_7 0x03 + +enum helene_tv_system_t { + SONY_HELENE_TV_SYSTEM_UNKNOWN, + /* Terrestrial Analog */ + SONY_HELENE_ATV_MN_EIAJ, + /**< System-M (Japan) (IF: Fp=5.75MHz in default) */ + SONY_HELENE_ATV_MN_SAP, + /**< System-M (US) (IF: Fp=5.75MHz in default) */ + SONY_HELENE_ATV_MN_A2, + /**< System-M (Korea) (IF: Fp=5.9MHz in default) */ + SONY_HELENE_ATV_BG, + /**< System-B/G (IF: Fp=7.3MHz in default) */ + SONY_HELENE_ATV_I, + /**< System-I (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_DK, + /**< System-D/K (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_L, + /**< System-L (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_L_DASH, + /**< System-L DASH (IF: Fp=2.2MHz in default) */ + /* Terrestrial/Cable Digital */ + SONY_HELENE_DTV_8VSB, + /**< ATSC 8VSB (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_QAM, + /**< US QAM (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_ISDBT_6, + /**< ISDB-T 6MHzBW (IF: Fc=3.55MHz in default) */ + SONY_HELENE_DTV_ISDBT_7, + /**< ISDB-T 7MHzBW (IF: Fc=4.15MHz in default) */ + SONY_HELENE_DTV_ISDBT_8, + /**< ISDB-T 8MHzBW (IF: Fc=4.75MHz in default) */ + SONY_HELENE_DTV_DVBT_5, + /**< DVB-T 5MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT_6, + /**< DVB-T 6MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT_7, + /**< DVB-T 7MHzBW (IF: Fc=4.2MHz in default) */ + SONY_HELENE_DTV_DVBT_8, + /**< DVB-T 8MHzBW (IF: Fc=4.8MHz in default) */ + SONY_HELENE_DTV_DVBT2_1_7, + /**< DVB-T2 1.7MHzBW (IF: Fc=3.5MHz in default) */ + SONY_HELENE_DTV_DVBT2_5, + /**< DVB-T2 5MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT2_6, + /**< DVB-T2 6MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT2_7, + /**< DVB-T2 7MHzBW (IF: Fc=4.2MHz in default) */ + SONY_HELENE_DTV_DVBT2_8, + /**< DVB-T2 8MHzBW (IF: Fc=4.8MHz in default) */ + SONY_HELENE_DTV_DVBC_6, + /**< DVB-C 6MHzBW (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_DVBC_8, + /**< DVB-C 8MHzBW (IF: Fc=4.9MHz in default) */ + SONY_HELENE_DTV_DVBC2_6, + /**< DVB-C2 6MHzBW (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_DVBC2_8, + /**< DVB-C2 8MHzBW (IF: Fc=4.9MHz in default) */ + SONY_HELENE_DTV_DTMB, + /**< DTMB (IF: Fc=5.1MHz in default) */ + /* Satellite */ + SONY_HELENE_STV_ISDBS, + /**< ISDB-S */ + SONY_HELENE_STV_DVBS, + /**< DVB-S */ + SONY_HELENE_STV_DVBS2, + /**< DVB-S2 */ + + SONY_HELENE_ATV_MIN = SONY_HELENE_ATV_MN_EIAJ, + /**< Minimum analog terrestrial system */ + SONY_HELENE_ATV_MAX = SONY_HELENE_ATV_L_DASH, + /**< Maximum analog terrestrial system */ + SONY_HELENE_DTV_MIN = SONY_HELENE_DTV_8VSB, + /**< Minimum digital terrestrial system */ + SONY_HELENE_DTV_MAX = SONY_HELENE_DTV_DTMB, + /**< Maximum digital terrestrial system */ + SONY_HELENE_TERR_TV_SYSTEM_NUM, + /**< Number of supported terrestrial broadcasting system */ + SONY_HELENE_STV_MIN = SONY_HELENE_STV_ISDBS, + /**< Minimum satellite system */ + SONY_HELENE_STV_MAX = SONY_HELENE_STV_DVBS2 + /**< Maximum satellite system */ +}; + +struct helene_terr_adjust_param_t { + /* < Addr:0x69 Bit[6:4] : RFVGA gain. + * 0xFF means Auto. (RF_GAIN_SEL = 1) + */ + uint8_t RF_GAIN; + /* < Addr:0x69 Bit[3:0] : IF_BPF gain. + */ + uint8_t IF_BPF_GC; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (FRF <= 172MHz) + */ + uint8_t RFOVLD_DET_LV1_VL; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (172MHz < FRF <= 464MHz) + */ + uint8_t RFOVLD_DET_LV1_VH; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (FRF > 464MHz) + */ + uint8_t RFOVLD_DET_LV1_U; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (FRF <= 172MHz) + */ + uint8_t IFOVLD_DET_LV_VL; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (172MHz < FRF <= 464MHz) + */ + uint8_t IFOVLD_DET_LV_VH; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (FRF > 464MHz) + */ + uint8_t IFOVLD_DET_LV_U; + /* < Addr:0x6D Bit[5:4] : + * IF filter center offset. + */ + uint8_t IF_BPF_F0; + /* < Addr:0x6D Bit[1:0] : + * 6MHzBW(0x00) or 7MHzBW(0x01) + * or 8MHzBW(0x02) or 1.7MHzBW(0x03) + */ + uint8_t BW; + /* < Addr:0x6E Bit[4:0] : + * 5bit signed. IF offset (kHz) = FIF_OFFSET x 50 + */ + uint8_t FIF_OFFSET; + /* < Addr:0x6F Bit[4:0] : + * 5bit signed. BW offset (kHz) = + * BW_OFFSET x 50 (BW_OFFSET x 10 in 1.7MHzBW) + */ + uint8_t BW_OFFSET; + /* < Addr:0x9C Bit[0] : + * Local polarity. (0: Upper Local, 1: Lower Local) + */ + uint8_t IS_LOWERLOCAL; +}; + +static const struct helene_terr_adjust_param_t +terr_params[SONY_HELENE_TERR_TV_SYSTEM_NUM] = { + /*< SONY_HELENE_TV_SYSTEM_UNKNOWN */ + {HELENE_AUTO, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(0), 0x00}, + /* Analog */ + /**< SONY_HELENE_ATV_MN_EIAJ (System-M (Japan)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00}, + /**< SONY_HELENE_ATV_MN_SAP (System-M (US)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00}, + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(3), HELENE_OFFSET(1), 0x00}, + /**< SONY_HELENE_ATV_MN_A2 (System-M (Korea)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_7, HELENE_OFFSET(11), HELENE_OFFSET(5), 0x00}, + /**< SONY_HELENE_ATV_BG (System-B/G) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_I (System-I) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_DK (System-D/K) */ + {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_L (System-L) */ + {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00, + HELENE_BW_8, HELENE_OFFSET(-1), HELENE_OFFSET(4), 0x00}, + /**< SONY_HELENE_ATV_L_DASH (System-L DASH) */ + /* Digital */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x03, 0x03, 0x03, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_8VSB (ATSC 8VSB) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_QAM (US QAM) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-9), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_6 (ISDB-T 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-7), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_7 (ISDB-T 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-5), HELENE_OFFSET(-7), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_8 (ISDB-T 8MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT_5 (DVB-T 5MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT_6 (DVB-T 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_DVBT_7 (DVB-T 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_DVBT_8 (DVB-T 8MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_1_7, HELENE_OFFSET(-10), HELENE_OFFSET(-10), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_1_7 (DVB-T2 1.7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_5 (DVB-T2 5MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_6 (DVB-T2 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_7 (DVB-T2 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_8 (DVB-T2 8MHzBW) */ + {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-4), 0x00}, + /**< SONY_HELENE_DTV_DVBC_6 (DVB-C 6MHzBW) */ + {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBC_8 (DVB-C 8MHzBW) */ + {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-2), 0x00}, + /**< SONY_HELENE_DTV_DVBC2_6 (DVB-C2 6MHzBW) */ + {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(0), 0x00}, + /**< SONY_HELENE_DTV_DVBC2_8 (DVB-C2 8MHzBW) */ + {HELENE_AUTO, 0x04, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(1), 0x00} + /**< SONY_HELENE_DTV_DTMB (DTMB) */ +}; + +static void helene_i2c_debug(struct helene_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "helene: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("helene: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int helene_write_regs(struct helene_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 > sizeof(buf)) { + dev_warn(&priv->i2c->dev, + "wr reg=%04x: len=%d vs %Zu is too big!\n", + reg, len + 1, sizeof(buf)); + return -E2BIG; + } + + helene_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int helene_write_reg(struct helene_priv *priv, u8 reg, u8 val) +{ + return helene_write_regs(priv, reg, &val, 1); +} + +static int helene_read_regs(struct helene_priv *priv, + u8 reg, u8 *val, u32 len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, &msg[0], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: I2C rw failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ret = i2c_transfer(priv->i2c, &msg[1], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + helene_i2c_debug(priv, reg, 0, val, len); + return 0; +} + +static int helene_read_reg(struct helene_priv *priv, u8 reg, u8 *val) +{ + return helene_read_regs(priv, reg, val, 1); +} + +static int helene_set_reg_bits(struct helene_priv *priv, + u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = helene_read_reg(priv, reg, &rdata); + if (res != 0) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return helene_write_reg(priv, reg, data); +} + +static int helene_enter_power_save(struct helene_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x0); + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC0); + + priv->state = STATE_SLEEP; + return 0; +} + +static int helene_leave_power_save(struct helene_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC4); + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x40); + + priv->state = STATE_ACTIVE; + return 0; +} + +static int helene_init(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return helene_leave_power_save(priv); +} + +static int helene_release(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int helene_sleep(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + helene_enter_power_save(priv); + return 0; +} + +static enum helene_tv_system_t helene_get_tv_system(struct dvb_frontend *fe) +{ + enum helene_tv_system_t system = SONY_HELENE_TV_SYSTEM_UNKNOWN; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + + if (p->delivery_system == SYS_DVBT) { + if (p->bandwidth_hz <= 5000000) + system = SONY_HELENE_DTV_DVBT_5; + else if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBT_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_DVBT_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBT_8; + else { + system = SONY_HELENE_DTV_DVBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBT2) { + if (p->bandwidth_hz <= 5000000) + system = SONY_HELENE_DTV_DVBT2_5; + else if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBT2_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_DVBT2_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBT2_8; + else { + system = SONY_HELENE_DTV_DVBT2_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBS) { + system = SONY_HELENE_STV_DVBS; + } else if (p->delivery_system == SYS_DVBS2) { + system = SONY_HELENE_STV_DVBS2; + } else if (p->delivery_system == SYS_ISDBS) { + system = SONY_HELENE_STV_ISDBS; + } else if (p->delivery_system == SYS_ISDBT) { + if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_ISDBT_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_ISDBT_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_ISDBT_8; + else { + system = SONY_HELENE_DTV_ISDBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A) { + if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBC_6; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBC_8; + } + dev_dbg(&priv->i2c->dev, + "%s(): HELENE DTV system %d (delsys %d, bandwidth %d)\n", + __func__, (int)system, p->delivery_system, + p->bandwidth_hz); + return system; +} + +static int helene_set_params_s(struct dvb_frontend *fe) +{ + u8 data[MAX_WRITE_REGSIZE]; + u32 frequency; + enum helene_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + int frequencykHz = p->frequency; + uint32_t frequency4kHz = 0; + u32 symbol_rate = p->symbol_rate/1000; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz sr=%uKsps\n", + __func__, frequencykHz, symbol_rate); + tv_system = helene_get_tv_system(fe); + + if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) { + dev_err(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + /* RF switch turn to satellite */ + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 0); + frequency = roundup(p->frequency / 1000, 1); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x15, 0x02); + + /* RFIN matching in power save (Sat) reset */ + helene_write_reg(priv, 0x43, 0x06); + + /* Analog block setting (0x6A, 0x6B) */ + data[0] = 0x00; + data[1] = 0x00; + helene_write_regs(priv, 0x6A, data, 2); + helene_write_reg(priv, 0x75, 0x99); + helene_write_reg(priv, 0x9D, 0x00); + + /* Tuning setting for CPU (0x61) */ + helene_write_reg(priv, 0x61, 0x07); + + /* Satellite mode select (0x01) */ + helene_write_reg(priv, 0x01, 0x01); + + /* Clock enable for internal logic block, CPU wake-up (0x04, 0x05) */ + data[0] = 0xC4; + data[1] = 0x40; + + switch (priv->xtal) { + case SONY_HELENE_XTAL_16000: + data[2] = 0x02; + break; + case SONY_HELENE_XTAL_20500: + data[2] = 0x02; + break; + case SONY_HELENE_XTAL_24000: + data[2] = 0x03; + break; + case SONY_HELENE_XTAL_41000: + data[2] = 0x05; + break; + default: + dev_err(&priv->i2c->dev, "%s(): unknown xtal %d\n", + __func__, priv->xtal); + return -EINVAL; + } + + /* Setting for analog block (0x07). LOOPFILTER INTERNAL */ + data[3] = 0x80; + + /* Tuning setting for analog block + * (0x08, 0x09, 0x0A, 0x0B). LOOPFILTER INTERNAL + */ + if (priv->xtal == SONY_HELENE_XTAL_20500) + data[4] = 0x58; + else + data[4] = 0x70; + + data[5] = 0x1E; + data[6] = 0x02; + data[7] = 0x24; + + /* Enable for analog block (0x0C, 0x0D, 0x0E). SAT LNA ON */ + data[8] = 0x0F; + data[8] |= 0xE0; /* POWERSAVE_TERR_RF_ACTIVE */ + data[9] = 0x02; + data[10] = 0x1E; + + /* Setting for LPF cutoff frequency (0x0F) */ + switch (tv_system) { + case SONY_HELENE_STV_ISDBS: + data[11] = 0x22; /* 22MHz */ + break; + case SONY_HELENE_STV_DVBS: + if (symbol_rate <= 4000) + data[11] = 0x05; + else if (symbol_rate <= 10000) + data[11] = (uint8_t)((symbol_rate * 47 + + (40000-1)) / 40000); + else + data[11] = (uint8_t)((symbol_rate * 27 + + (40000-1)) / 40000 + 5); + + if (data[11] > 36) + data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */ + break; + case SONY_HELENE_STV_DVBS2: + if (symbol_rate <= 4000) + data[11] = 0x05; + else if (symbol_rate <= 10000) + data[11] = (uint8_t)((symbol_rate * 11 + + (10000-1)) / 10000); + else + data[11] = (uint8_t)((symbol_rate * 3 + + (5000-1)) / 5000 + 5); + + if (data[11] > 36) + data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */ + break; + default: + dev_err(&priv->i2c->dev, "%s(): unknown standard %d\n", + __func__, tv_system); + return -EINVAL; + } + + /* RF tuning frequency setting (0x10, 0x11, 0x12) */ + frequency4kHz = (frequencykHz + 2) / 4; + data[12] = (uint8_t)(frequency4kHz & 0xFF); /* FRF_L */ + data[13] = (uint8_t)((frequency4kHz >> 8) & 0xFF); /* FRF_M */ + /* FRF_H (bit[3:0]) */ + data[14] = (uint8_t)((frequency4kHz >> 16) & 0x0F); + + /* Tuning command (0x13) */ + data[15] = 0xFF; + + /* Setting for IQOUT_LIMIT (0x14) 0.75Vpp */ + data[16] = 0x00; + + /* Enable IQ output (0x15) */ + data[17] = 0x01; + + helene_write_regs(priv, 0x04, data, 18); + + dev_dbg(&priv->i2c->dev, "%s(): tune done\n", + __func__); + + priv->frequency = frequency; + return 0; +} + +static int helene_set_params(struct dvb_frontend *fe) +{ + u8 data[MAX_WRITE_REGSIZE]; + u32 frequency; + enum helene_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + int frequencykHz = p->frequency / 1000; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n", + __func__, frequencykHz); + tv_system = helene_get_tv_system(fe); + + if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) { + dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 1); + frequency = roundup(p->frequency / 1000, 25); + + /* mode select */ + helene_write_reg(priv, 0x01, 0x00); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x74, 0x02); + + if (priv->state == STATE_SLEEP) + helene_leave_power_save(priv); + + /* Initial setting for internal analog block (0x91, 0x92) */ + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[0] = 0x16; + data[1] = 0x26; + } else { + data[0] = 0x10; + data[1] = 0x20; + } + helene_write_regs(priv, 0x91, data, 2); + + /* Setting for analog block */ + if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) + data[0] = 0x90; + else + data[0] = 0x00; + + /* Setting for local polarity (0x9D) */ + data[1] = (uint8_t)(terr_params[tv_system].IS_LOWERLOCAL & 0x01); + helene_write_regs(priv, 0x9C, data, 2); + + /* Enable for analog block */ + data[0] = 0xEE; + data[1] = 0x02; + data[2] = 0x1E; + data[3] = 0x67; /* Tuning setting for CPU */ + + /* Setting for PLL reference divider for xtal=24MHz */ + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) + data[4] = 0x18; + else + data[4] = 0x03; + + /* Tuning setting for analog block */ + if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) { + data[5] = 0x38; + data[6] = 0x1E; + data[7] = 0x02; + data[8] = 0x24; + } else if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[5] = 0x1C; + data[6] = 0x78; + data[7] = 0x08; + data[8] = 0x1C; + } else { + data[5] = 0xB4; + data[6] = 0x78; + data[7] = 0x08; + data[8] = 0x30; + } + helene_write_regs(priv, 0x5E, data, 9); + + /* LT_AMP_EN should be 0 */ + helene_set_reg_bits(priv, 0x67, 0x0, 0x02); + + /* Setting for IFOUT_LIMIT */ + data[0] = 0x00; /* 1.5Vpp */ + + /* RF_GAIN setting */ + if (terr_params[tv_system].RF_GAIN == HELENE_AUTO) + data[1] = 0x80; /* RF_GAIN_SEL = 1 */ + else + data[1] = (uint8_t)((terr_params[tv_system].RF_GAIN + << 4) & 0x70); + + /* IF_BPF_GC setting */ + data[1] |= (uint8_t)(terr_params[tv_system].IF_BPF_GC & 0x0F); + + /* Setting for internal RFAGC (0x6A, 0x6B, 0x6C) */ + data[2] = 0x00; + if (frequencykHz <= 172000) { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VL + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VL + & 0x07); + } else if (frequencykHz <= 464000) { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VH + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VH + & 0x07); + } else { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_U + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_U + & 0x07); + } + data[4] |= 0x20; + + /* Setting for IF frequency and bandwidth */ + + /* IF filter center frequency offset (IF_BPF_F0) (0x6D) */ + data[5] = (uint8_t)((terr_params[tv_system].IF_BPF_F0 << 4) & 0x30); + + /* IF filter band width (BW) (0x6D) */ + data[5] |= (uint8_t)(terr_params[tv_system].BW & 0x03); + + /* IF frequency offset value (FIF_OFFSET) (0x6E) */ + data[6] = (uint8_t)(terr_params[tv_system].FIF_OFFSET & 0x1F); + + /* IF band width offset value (BW_OFFSET) (0x6F) */ + data[7] = (uint8_t)(terr_params[tv_system].BW_OFFSET & 0x1F); + + /* RF tuning frequency setting (0x70, 0x71, 0x72) */ + data[8] = (uint8_t)(frequencykHz & 0xFF); /* FRF_L */ + data[9] = (uint8_t)((frequencykHz >> 8) & 0xFF); /* FRF_M */ + data[10] = (uint8_t)((frequencykHz >> 16) + & 0x0F); /* FRF_H (bit[3:0]) */ + + /* Tuning command */ + data[11] = 0xFF; + + /* Enable IF output, AGC and IFOUT pin selection (0x74) */ + data[12] = 0x01; + + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[13] = 0xD9; + data[14] = 0x0F; + data[15] = 0x24; + data[16] = 0x87; + } else { + data[13] = 0x99; + data[14] = 0x00; + data[15] = 0x24; + data[16] = 0x87; + } + + helene_write_regs(priv, 0x68, data, 17); + + dev_dbg(&priv->i2c->dev, "%s(): tune done\n", + __func__); + + priv->frequency = frequency; + return 0; +} + +static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct helene_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency * 1000; + return 0; +} + +static struct dvb_tuner_ops helene_tuner_ops = { + .info = { + .name = "Sony HELENE Ter tuner", + .frequency_min = 1000000, + .frequency_max = 1200000000, + .frequency_step = 25000, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params, + .get_frequency = helene_get_frequency, +}; + +static struct dvb_tuner_ops helene_tuner_ops_s = { + .info = { + .name = "Sony HELENE Sat tuner", + .frequency_min = 500000, + .frequency_max = 2500000, + .frequency_step = 1000, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params_s, + .get_frequency = helene_get_frequency, +}; + +/* power-on tuner + * call once after reset + */ +static int helene_x_pon(struct helene_priv *priv) +{ + /* RFIN matching in power save (terrestrial) = ACTIVE */ + /* RFIN matching in power save (satellite) = ACTIVE */ + u8 dataT[] = { 0x06, 0x00, 0x02, 0x00 }; + /* SAT_RF_ACTIVE = true, lnaOff = false, terrRfActive = true */ + u8 dataS[] = { 0x05, 0x06 }; + u8 cdata[] = {0x7A, 0x01}; + u8 data[20]; + u8 rdata[2]; + + /* mode select */ + helene_write_reg(priv, 0x01, 0x00); + + helene_write_reg(priv, 0x67, dataT[3]); + helene_write_reg(priv, 0x43, dataS[1]); + helene_write_regs(priv, 0x5E, dataT, 3); + helene_write_reg(priv, 0x0C, dataS[0]); + + /* Initial setting for internal logic block */ + helene_write_regs(priv, 0x99, cdata, sizeof(cdata)); + + /* 0x81 - 0x94 */ + data[0] = 0x18; /* xtal 24 MHz */ + data[1] = (uint8_t)(0x80 | (0x04 & 0x1F)); /* 4 x 25 = 100uA */ + data[2] = (uint8_t)(0x80 | (0x26 & 0x7F)); /* 38 x 0.25 = 9.5pF */ + data[3] = 0x80; /* REFOUT signal output 500mVpp */ + data[4] = 0x00; /* GPIO settings */ + data[5] = 0x00; /* GPIO settings */ + data[6] = 0xC4; /* Clock enable for internal logic block */ + data[7] = 0x40; /* Start CPU boot-up */ + data[8] = 0x10; /* For burst-write */ + + /* Setting for internal RFAGC */ + data[9] = 0x00; + data[10] = 0x45; + data[11] = 0x75; + + data[12] = 0x07; /* Setting for analog block */ + + /* Initial setting for internal analog block */ + data[13] = 0x1C; + data[14] = 0x3F; + data[15] = 0x02; + data[16] = 0x10; + data[17] = 0x20; + data[18] = 0x0A; + data[19] = 0x00; + + helene_write_regs(priv, 0x81, data, sizeof(data)); + + /* Setting for internal RFAGC */ + helene_write_reg(priv, 0x9B, 0x00); + + msleep(20); + + /* Check CPU_STT/CPU_ERR */ + helene_read_regs(priv, 0x1A, rdata, sizeof(rdata)); + + if (rdata[0] != 0x00) { + dev_err(&priv->i2c->dev, + "HELENE tuner CPU error 0x%x\n", rdata[0]); + return -EIO; + } + + /* VCO current setting */ + cdata[0] = 0x90; + cdata[1] = 0x06; + helene_write_regs(priv, 0x17, cdata, sizeof(cdata)); + msleep(20); + helene_read_reg(priv, 0x19, data); + helene_write_reg(priv, 0x95, (uint8_t)((data[0] >> 4) & 0x0F)); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x74, 0x02); + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x00); + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC0); + + /* Load capacitance control setting for crystal oscillator */ + helene_write_reg(priv, 0x80, 0x01); + + /* Satellite initial setting */ + cdata[0] = 0x07; + cdata[1] = 0x00; + helene_write_regs(priv, 0x41, cdata, sizeof(cdata)); + + dev_info(&priv->i2c->dev, + "HELENE tuner x_pon done\n"); + + return 0; +} + +struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + struct helene_priv *priv = NULL; + + priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) + return NULL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_s, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HELENE Sat attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(helene_attach_s); + +struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + struct helene_priv *priv = NULL; + + priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) + return NULL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HELENE Ter attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(helene_attach); + +MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver"); +MODULE_AUTHOR("Abylay Ospan <aospan@netup.ru>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h new file mode 100644 index 000000000000..e1b9224cfc55 --- /dev/null +++ b/drivers/media/dvb-frontends/helene.h @@ -0,0 +1,79 @@ +/* + * helene.h + * + * Sony HELENE DVB-S/S2/T/T2/C/C2/ISDB-T/S tuner driver (CXD2858ER) + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + * + * 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 __DVB_HELENE_H__ +#define __DVB_HELENE_H__ + +#include <linux/kconfig.h> +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +enum helene_xtal { + SONY_HELENE_XTAL_16000, /* 16 MHz */ + SONY_HELENE_XTAL_20500, /* 20.5 MHz */ + SONY_HELENE_XTAL_24000, /* 24 MHz */ + SONY_HELENE_XTAL_41000 /* 41 MHz */ +}; + +/** + * struct helene_config - the configuration of 'Helene' tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + */ +struct helene_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); + enum helene_xtal xtal; +}; + +#if IS_REACHABLE(CONFIG_DVB_HELENE) +extern struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#if IS_REACHABLE(CONFIG_DVB_HELENE) +extern struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c index 000606af70f7..a98bca5270d9 100644 --- a/drivers/media/dvb-frontends/horus3a.c +++ b/drivers/media/dvb-frontends/horus3a.c @@ -66,7 +66,7 @@ static int horus3a_write_regs(struct horus3a_priv *priv, } }; - if (len + 1 >= sizeof(buf)) { + if (len + 1 > sizeof(buf)) { dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", reg, len + 1); return -E2BIG; @@ -272,24 +272,6 @@ static int horus3a_set_params(struct dvb_frontend *fe) if (fc_lpf > 36) fc_lpf = 36; } else if (p->delivery_system == SYS_DVBS2) { - int rolloff; - - switch (p->rolloff) { - case ROLLOFF_35: - rolloff = 35; - break; - case ROLLOFF_25: - rolloff = 25; - break; - case ROLLOFF_20: - rolloff = 20; - break; - case ROLLOFF_AUTO: - default: - dev_err(&priv->i2c->dev, - "horus3a: auto roll-off is not supported\n"); - return -EINVAL; - } /* * SR <= 4.5: * fc_lpf = 5 @@ -302,11 +284,9 @@ static int horus3a_set_params(struct dvb_frontend *fe) if (symbol_rate <= 4500) fc_lpf = 5; else if (symbol_rate <= 10000) - fc_lpf = (u8)DIV_ROUND_UP( - symbol_rate * (200 + rolloff), 200000); + fc_lpf = (u8)((symbol_rate * 11 + (10000-1)) / 10000); else - fc_lpf = (u8)DIV_ROUND_UP( - symbol_rate * (100 + rolloff), 200000) + 5; + fc_lpf = (u8)((symbol_rate * 3 + (5000-1)) / 5000 + 5); /* 5 <= fc_lpf <= 36 is valid */ if (fc_lpf > 36) fc_lpf = 36; diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index a09b12313a73..ef79a4ec31e2 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -609,7 +609,7 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) { struct m88rs2000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - enum fe_status status; + enum fe_status status = 0; int i, ret = 0; u32 tuner_freq; s16 offset = 0; diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/media/dvb-frontends/mn88472.c index 7ea749cf19f9..18fb2df1e2bd 100644 --- a/drivers/staging/media/mn88472/mn88472.c +++ b/drivers/media/dvb-frontends/mn88472.c @@ -17,28 +17,90 @@ #include "mn88472_priv.h" static int mn88472_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *s) + struct dvb_frontend_tune_settings *s) { - s->min_delay_ms = 800; + s->min_delay_ms = 1000; return 0; } +static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int utmp; + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + ret = regmap_read(dev->regmap[0], 0x7f, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x09) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + case SYS_DVBT2: + ret = regmap_read(dev->regmap[2], 0x92, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x0d) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else if ((utmp & 0x0f) >= 0x0a) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + else if ((utmp & 0x0f) >= 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status = 0; + break; + case SYS_DVBC_ANNEX_A: + ret = regmap_read(dev->regmap[1], 0x84, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x08) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + default: + ret = -EINVAL; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + static int mn88472_set_frontend(struct dvb_frontend *fe) { struct i2c_client *client = fe->demodulator_priv; struct mn88472_dev *dev = i2c_get_clientdata(client); struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; - u32 if_frequency = 0; - u64 tmp; - u8 delivery_system_val, if_val[3], bw_val[7], bw_val2; + unsigned int utmp; + u32 if_frequency; + u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr; + u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val; + u8 reg_bank0_d6_val; dev_dbg(&client->dev, - "delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d\n", - c->delivery_system, c->modulation, - c->frequency, c->symbol_rate, c->inversion); + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); - if (!dev->warm) { + if (!dev->active) { ret = -EAGAIN; goto err; } @@ -46,39 +108,64 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) switch (c->delivery_system) { case SYS_DVBT: delivery_system_val = 0x02; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x1f; + reg_bank0_d4_val = 0x0a; + reg_bank0_d6_val = 0x48; break; case SYS_DVBT2: delivery_system_val = 0x03; + reg_bank0_b4_val = 0xf6; + reg_bank0_cd_val = 0x01; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x46; break; case SYS_DVBC_ANNEX_A: delivery_system_val = 0x04; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x17; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x48; break; default: ret = -EINVAL; goto err; } - if (c->bandwidth_hz <= 5000000) { - memcpy(bw_val, "\xe5\x99\x9a\x1b\xa9\x1b\xa9", 7); - bw_val2 = 0x03; - } else if (c->bandwidth_hz <= 6000000) { - /* IF 3570000 Hz, BW 6000000 Hz */ - memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7); - bw_val2 = 0x02; - } else if (c->bandwidth_hz <= 7000000) { - /* IF 4570000 Hz, BW 7000000 Hz */ - memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7); - bw_val2 = 0x01; - } else if (c->bandwidth_hz <= 8000000) { - /* IF 4570000 Hz, BW 8000000 Hz */ - memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7); - bw_val2 = 0x00; - } else { - ret = -EINVAL; - goto err; + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + switch (c->bandwidth_hz) { + case 5000000: + bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9"; + bandwidth_val = 0x03; + break; + case 6000000: + bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b"; + bandwidth_val = 0x02; + break; + case 7000000: + bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c"; + bandwidth_val = 0x01; + break; + case 8000000: + bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee"; + bandwidth_val = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + break; + case SYS_DVBC_ANNEX_A: + bandwidth_vals_ptr = NULL; + bandwidth_val = 0x00; + break; + default: + break; } - /* program tuner */ + /* Program tuner */ if (fe->ops.tuner_ops.set_params) { ret = fe->ops.tuner_ops.set_params(fe); if (ret) @@ -91,20 +178,10 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) goto err; dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); - } - - /* Calculate IF registers ( (1<<24)*IF / Xtal ) */ - tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2), - dev->xtal); - if_val[0] = (tmp >> 16) & 0xff; - if_val[1] = (tmp >> 8) & 0xff; - if_val[2] = (tmp >> 0) & 0xff; - - ret = regmap_write(dev->regmap[2], 0xfb, 0x13); - ret = regmap_write(dev->regmap[2], 0xef, 0x13); - ret = regmap_write(dev->regmap[2], 0xf9, 0x13); - if (ret) + } else { + ret = -EINVAL; goto err; + } ret = regmap_write(dev->regmap[2], 0x00, 0x66); if (ret) @@ -118,157 +195,81 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); if (ret) goto err; - ret = regmap_write(dev->regmap[2], 0x04, bw_val2); + ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val); if (ret) goto err; - for (i = 0; i < sizeof(if_val); i++) { - ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]); + /* IF */ + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk); + buf[0] = (utmp >> 16) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 0) & 0xff; + for (i = 0; i < 3; i++) { + ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]); if (ret) goto err; } - for (i = 0; i < sizeof(bw_val); i++) { - ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]); - if (ret) - goto err; + /* Bandwidth */ + if (bandwidth_vals_ptr) { + for (i = 0; i < 7; i++) { + ret = regmap_write(dev->regmap[2], 0x13 + i, + bandwidth_vals_ptr[i]); + if (ret) + goto err; + } } + ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val); + if (ret) + goto err; + switch (c->delivery_system) { case SYS_DVBT: ret = regmap_write(dev->regmap[0], 0x07, 0x26); - ret = regmap_write(dev->regmap[0], 0xb0, 0x0a); - ret = regmap_write(dev->regmap[0], 0xb4, 0x00); - ret = regmap_write(dev->regmap[0], 0xcd, 0x1f); - ret = regmap_write(dev->regmap[0], 0xd4, 0x0a); - ret = regmap_write(dev->regmap[0], 0xd6, 0x48); + if (ret) + goto err; ret = regmap_write(dev->regmap[0], 0x00, 0xba); + if (ret) + goto err; ret = regmap_write(dev->regmap[0], 0x01, 0x13); if (ret) goto err; break; case SYS_DVBT2: ret = regmap_write(dev->regmap[2], 0x2b, 0x13); + if (ret) + goto err; ret = regmap_write(dev->regmap[2], 0x4f, 0x05); + if (ret) + goto err; ret = regmap_write(dev->regmap[1], 0xf6, 0x05); - ret = regmap_write(dev->regmap[0], 0xb0, 0x0a); - ret = regmap_write(dev->regmap[0], 0xb4, 0xf6); - ret = regmap_write(dev->regmap[0], 0xcd, 0x01); - ret = regmap_write(dev->regmap[0], 0xd4, 0x09); - ret = regmap_write(dev->regmap[0], 0xd6, 0x46); - ret = regmap_write(dev->regmap[2], 0x30, 0x80); - ret = regmap_write(dev->regmap[2], 0x32, 0x00); if (ret) goto err; - break; - case SYS_DVBC_ANNEX_A: - ret = regmap_write(dev->regmap[0], 0xb0, 0x0b); - ret = regmap_write(dev->regmap[0], 0xb4, 0x00); - ret = regmap_write(dev->regmap[0], 0xcd, 0x17); - ret = regmap_write(dev->regmap[0], 0xd4, 0x09); - ret = regmap_write(dev->regmap[0], 0xd6, 0x48); - ret = regmap_write(dev->regmap[1], 0x00, 0xb0); + ret = regmap_write(dev->regmap[2], 0x32, c->stream_id); if (ret) goto err; break; - default: - ret = -EINVAL; - goto err; - } - - ret = regmap_write(dev->regmap[0], 0x46, 0x00); - ret = regmap_write(dev->regmap[0], 0xae, 0x00); - - switch (dev->ts_mode) { - case SERIAL_TS_MODE: - ret = regmap_write(dev->regmap[2], 0x08, 0x1d); - break; - case PARALLEL_TS_MODE: - ret = regmap_write(dev->regmap[2], 0x08, 0x00); + case SYS_DVBC_ANNEX_A: break; default: - dev_dbg(&client->dev, "ts_mode error: %d\n", dev->ts_mode); - ret = -EINVAL; - goto err; - } - - switch (dev->ts_clock) { - case VARIABLE_TS_CLOCK: - ret = regmap_write(dev->regmap[0], 0xd9, 0xe3); break; - case FIXED_TS_CLOCK: - ret = regmap_write(dev->regmap[0], 0xd9, 0xe1); - break; - default: - dev_dbg(&client->dev, "ts_clock error: %d\n", dev->ts_clock); - ret = -EINVAL; - goto err; } - /* Reset demod */ + /* Reset FSM */ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); if (ret) goto err; - dev->delivery_system = c->delivery_system; - - return 0; -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} - -static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) -{ - struct i2c_client *client = fe->demodulator_priv; - struct mn88472_dev *dev = i2c_get_clientdata(client); - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret; - unsigned int utmp; - int lock = 0; - - *status = 0; - - if (!dev->warm) { - ret = -EAGAIN; - goto err; - } - - switch (c->delivery_system) { - case SYS_DVBT: - ret = regmap_read(dev->regmap[0], 0x7F, &utmp); - if (ret) - goto err; - if ((utmp & 0xF) >= 0x09) - lock = 1; - break; - case SYS_DVBT2: - ret = regmap_read(dev->regmap[2], 0x92, &utmp); - if (ret) - goto err; - if ((utmp & 0xF) >= 0x07) - *status |= FE_HAS_SIGNAL; - if ((utmp & 0xF) >= 0x0a) - *status |= FE_HAS_CARRIER; - if ((utmp & 0xF) >= 0x0d) - *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - break; - case SYS_DVBC_ANNEX_A: - ret = regmap_read(dev->regmap[1], 0x84, &utmp); - if (ret) - goto err; - if ((utmp & 0xF) >= 0x08) - lock = 1; - break; - default: - ret = -EINVAL; - goto err; - } - - if (lock) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | - FE_HAS_SYNC | FE_HAS_LOCK; - return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -279,93 +280,107 @@ static int mn88472_init(struct dvb_frontend *fe) { struct i2c_client *client = fe->demodulator_priv; struct mn88472_dev *dev = i2c_get_clientdata(client); - int ret, len, remaining; - const struct firmware *fw = NULL; - u8 *fw_file = MN88472_FIRMWARE; - unsigned int tmp; + int ret, len, rem; + unsigned int utmp; + const struct firmware *firmware; + const char *name = MN88472_FIRMWARE; dev_dbg(&client->dev, "\n"); - /* set cold state by default */ - dev->warm = false; - - /* power on */ + /* Power up */ ret = regmap_write(dev->regmap[2], 0x05, 0x00); if (ret) goto err; - - ret = regmap_bulk_write(dev->regmap[2], 0x0b, "\x00\x00", 2); + ret = regmap_write(dev->regmap[2], 0x0b, 0x00); if (ret) goto err; - - /* check if firmware is already running */ - ret = regmap_read(dev->regmap[0], 0xf5, &tmp); + ret = regmap_write(dev->regmap[2], 0x0c, 0x00); if (ret) goto err; - if (!(tmp & 0x1)) { - dev_info(&client->dev, "firmware already running\n"); - dev->warm = true; - return 0; - } + /* Check if firmware is already running */ + ret = regmap_read(dev->regmap[0], 0xf5, &utmp); + if (ret) + goto err; + if (!(utmp & 0x01)) + goto warm; - /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, &client->dev); + ret = request_firmware(&firmware, name, &client->dev); if (ret) { - dev_err(&client->dev, "firmare file '%s' not found\n", - fw_file); + dev_err(&client->dev, "firmware file '%s' not found\n", name); goto err; } - dev_info(&client->dev, "downloading firmware from file '%s'\n", - fw_file); + dev_info(&client->dev, "downloading firmware from file '%s'\n", name); ret = regmap_write(dev->regmap[0], 0xf5, 0x03); if (ret) - goto firmware_release; - - for (remaining = fw->size; remaining > 0; - remaining -= (dev->i2c_wr_max - 1)) { - len = remaining; - if (len > (dev->i2c_wr_max - 1)) - len = dev->i2c_wr_max - 1; + goto err_release_firmware; + for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) { + len = min(dev->i2c_write_max - 1, rem); ret = regmap_bulk_write(dev->regmap[0], 0xf6, - &fw->data[fw->size - remaining], len); + &firmware->data[firmware->size - rem], + len); if (ret) { - dev_err(&client->dev, - "firmware download failed=%d\n", ret); - goto firmware_release; + dev_err(&client->dev, "firmware download failed %d\n", + ret); + goto err_release_firmware; } } - /* parity check of firmware */ - ret = regmap_read(dev->regmap[0], 0xf8, &tmp); - if (ret) { - dev_err(&client->dev, - "parity reg read failed=%d\n", ret); - goto firmware_release; - } - if (tmp & 0x10) { - dev_err(&client->dev, - "firmware parity check failed=0x%x\n", tmp); - goto firmware_release; + /* Parity check of firmware */ + ret = regmap_read(dev->regmap[0], 0xf8, &utmp); + if (ret) + goto err_release_firmware; + if (utmp & 0x10) { + ret = -EINVAL; + dev_err(&client->dev, "firmware did not run\n"); + goto err_release_firmware; } - dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp); ret = regmap_write(dev->regmap[0], 0xf5, 0x00); if (ret) - goto firmware_release; + goto err_release_firmware; + + release_firmware(firmware); +warm: + /* TS config */ + switch (dev->ts_mode) { + case SERIAL_TS_MODE: + utmp = 0x1d; + break; + case PARALLEL_TS_MODE: + utmp = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[2], 0x08, utmp); + if (ret) + goto err; - release_firmware(fw); - fw = NULL; + switch (dev->ts_clk) { + case VARIABLE_TS_CLOCK: + utmp = 0xe3; + break; + case FIXED_TS_CLOCK: + utmp = 0xe1; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[0], 0xd9, utmp); + if (ret) + goto err; - /* warm state */ - dev->warm = true; + dev->active = true; return 0; -firmware_release: - release_firmware(fw); +err_release_firmware: + release_firmware(firmware); err: dev_dbg(&client->dev, "failed=%d\n", ret); return ret; @@ -379,18 +394,17 @@ static int mn88472_sleep(struct dvb_frontend *fe) dev_dbg(&client->dev, "\n"); - /* power off */ + /* Power down */ + ret = regmap_write(dev->regmap[2], 0x0c, 0x30); + if (ret) + goto err; ret = regmap_write(dev->regmap[2], 0x0b, 0x30); - if (ret) goto err; - ret = regmap_write(dev->regmap[2], 0x05, 0x3e); if (ret) goto err; - dev->delivery_system = SYS_UNDEFINED; - return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -434,10 +448,19 @@ static struct dvb_frontend_ops mn88472_ops = { .read_status = mn88472_read_status, }; +static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client) +{ + struct mn88472_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + static int mn88472_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { - struct mn88472_config *config = client->dev.platform_data; + struct mn88472_config *pdata = client->dev.platform_data; struct mn88472_dev *dev; int ret; unsigned int utmp; @@ -448,23 +471,16 @@ static int mn88472_probe(struct i2c_client *client, dev_dbg(&client->dev, "\n"); - /* Caller really need to provide pointer for frontend we create. */ - if (config->fe == NULL) { - dev_err(&client->dev, "frontend pointer not defined\n"); - ret = -EINVAL; - goto err; - } - dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto err; } - dev->i2c_wr_max = config->i2c_wr_max; - dev->xtal = config->xtal; - dev->ts_mode = config->ts_mode; - dev->ts_clock = config->ts_clock; + dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0; + dev->clk = pdata->xtal; + dev->ts_mode = pdata->ts_mode; + dev->ts_clk = pdata->ts_clock; dev->client[0] = client; dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); if (IS_ERR(dev->regmap[0])) { @@ -472,15 +488,25 @@ static int mn88472_probe(struct i2c_client *client, goto err_kfree; } - /* check demod answers to I2C */ - ret = regmap_read(dev->regmap[0], 0x00, &utmp); + /* Check demod answers with correct chip id */ + ret = regmap_read(dev->regmap[0], 0xff, &utmp); if (ret) goto err_regmap_0_regmap_exit; + dev_dbg(&client->dev, "chip id=%02x\n", utmp); + + if (utmp != 0x02) { + ret = -ENODEV; + goto err_regmap_0_regmap_exit; + } + /* - * Chip has three I2C addresses for different register pages. Used + * Chip has three I2C addresses for different register banks. Used * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, - * 0x1a and 0x1c, in order to get own I2C client for each register page. + * 0x1a and 0x1c, in order to get own I2C client for each register bank. + * + * Also, register bank 2 do not support sequential I/O. Only single + * register write or read is allowed to that bank. */ dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); if (!dev->client[1]) { @@ -510,15 +536,25 @@ static int mn88472_probe(struct i2c_client *client, } i2c_set_clientdata(dev->client[2], dev); - /* create dvb_frontend */ + /* Sleep because chip is active by default */ + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err_regmap_2_regmap_exit; + + /* Create dvb frontend */ memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops)); dev->fe.demodulator_priv = client; - *config->fe = &dev->fe; + *pdata->fe = &dev->fe; i2c_set_clientdata(client, dev); - dev_info(&client->dev, "Panasonic MN88472 successfully attached\n"); - return 0; + /* Setup callbacks */ + pdata->get_dvb_frontend = mn88472_get_dvb_frontend; + dev_info(&client->dev, "Panasonic MN88472 successfully identified\n"); + + return 0; +err_regmap_2_regmap_exit: + regmap_exit(dev->regmap[2]); err_client_2_i2c_unregister_device: i2c_unregister_device(dev->client[2]); err_regmap_1_regmap_exit: @@ -561,11 +597,12 @@ MODULE_DEVICE_TABLE(i2c, mn88472_id_table); static struct i2c_driver mn88472_driver = { .driver = { - .name = "mn88472", + .name = "mn88472", + .suppress_bind_attrs = true, }, - .probe = mn88472_probe, - .remove = mn88472_remove, - .id_table = mn88472_id_table, + .probe = mn88472_probe, + .remove = mn88472_remove, + .id_table = mn88472_id_table, }; module_i2c_driver(mn88472_driver); diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h index 095294d292f3..323632523876 100644 --- a/drivers/media/dvb-frontends/mn88472.h +++ b/drivers/media/dvb-frontends/mn88472.h @@ -19,23 +19,33 @@ #include <linux/dvb/frontend.h> -enum ts_clock { - VARIABLE_TS_CLOCK, - FIXED_TS_CLOCK, -}; +/** + * struct mn88472_config - Platform data for the mn88472 driver + * @xtal: Clock frequency. + * @ts_mode: TS mode. + * @ts_clock: TS clock config. + * @i2c_wr_max: Max number of bytes driver writes to I2C at once. + * @get_dvb_frontend: Get DVB frontend. + */ -enum ts_mode { - SERIAL_TS_MODE, - PARALLEL_TS_MODE, -}; +/* Define old names for backward compatibility */ +#define VARIABLE_TS_CLOCK MN88472_TS_CLK_VARIABLE +#define FIXED_TS_CLOCK MN88472_TS_CLK_FIXED +#define SERIAL_TS_MODE MN88472_TS_MODE_SERIAL +#define PARALLEL_TS_MODE MN88472_TS_MODE_PARALLEL struct mn88472_config { - /* - * Max num of bytes given I2C adapter could write at once. - * Default: none - */ - u16 i2c_wr_max; + unsigned int xtal; + +#define MN88472_TS_MODE_SERIAL 0 +#define MN88472_TS_MODE_PARALLEL 1 + int ts_mode; +#define MN88472_TS_CLK_FIXED 0 +#define MN88472_TS_CLK_VARIABLE 1 + int ts_clock; + + u16 i2c_wr_max; /* Everything after that is returned by the driver. */ @@ -43,14 +53,7 @@ struct mn88472_config { * DVB frontend. */ struct dvb_frontend **fe; - - /* - * Xtal frequency. - * Hz - */ - u32 xtal; - int ts_mode; - int ts_clock; + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); }; #endif diff --git a/drivers/staging/media/mn88472/mn88472_priv.h b/drivers/media/dvb-frontends/mn88472_priv.h index 1a0de9e46b66..cdf2597a25d1 100644 --- a/drivers/staging/media/mn88472/mn88472_priv.h +++ b/drivers/media/dvb-frontends/mn88472_priv.h @@ -28,12 +28,11 @@ struct mn88472_dev { struct i2c_client *client[3]; struct regmap *regmap[3]; struct dvb_frontend fe; - u16 i2c_wr_max; - enum fe_delivery_system delivery_system; - bool warm; /* FW running */ - u32 xtal; - int ts_mode; - int ts_clock; + u16 i2c_write_max; + unsigned int clk; + unsigned int active:1; + unsigned int ts_mode:1; + unsigned int ts_clk:1; }; #endif diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c index 6c5d592161d4..451974a1d7ed 100644 --- a/drivers/media/dvb-frontends/mn88473.c +++ b/drivers/media/dvb-frontends/mn88473.c @@ -330,7 +330,7 @@ static int mn88473_init(struct dvb_frontend *fe) /* Request the firmware, this will block and timeout */ ret = request_firmware(&fw, name, &client->dev); if (ret) { - dev_err(&client->dev, "firmare file '%s' not found\n", name); + dev_err(&client->dev, "firmware file '%s' not found\n", name); goto err; } @@ -536,7 +536,7 @@ static int mn88473_probe(struct i2c_client *client, /* Sleep because chip is active by default */ ret = regmap_write(dev->regmap[2], 0x05, 0x3e); if (ret) - goto err_client_2_i2c_unregister_device; + goto err_regmap_2_regmap_exit; /* Create dvb frontend */ memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops)); @@ -547,7 +547,8 @@ static int mn88473_probe(struct i2c_client *client, dev_info(&client->dev, "Panasonic MN88473 successfully identified\n"); return 0; - +err_regmap_2_regmap_exit: + regmap_exit(dev->regmap[2]); err_client_2_i2c_unregister_device: i2c_unregister_device(dev->client[2]); err_regmap_1_regmap_exit: diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index bfb6beedd40b..c16c69e9d26c 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -947,6 +947,8 @@ static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable) goto err; } + dev->slave_ts = enable; + return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -960,7 +962,7 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) int ret; u8 u8tmp; - dev_dbg(&client->dev, "onoff=%d\n", onoff); + dev_dbg(&client->dev, "onoff=%d, slave_ts=%d\n", onoff, dev->slave_ts); /* enable / disable PID filter */ if (onoff) @@ -968,7 +970,10 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) else u8tmp = 0x00; - ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp); + if (dev->slave_ts) + ret = regmap_update_bits(dev->regmap, 0x021, 0xc0, u8tmp); + else + ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp); if (ret) goto err; @@ -986,8 +991,8 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, int ret; u8 buf[4]; - dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n", - index, pid, onoff); + dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d slave_ts=%d\n", + index, pid, onoff, dev->slave_ts); /* skip invalid PIDs (0x2000) */ if (pid > 0x1fff || index > 32) @@ -1003,14 +1008,22 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, buf[1] = (dev->filters >> 8) & 0xff; buf[2] = (dev->filters >> 16) & 0xff; buf[3] = (dev->filters >> 24) & 0xff; - ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4); + + if (dev->slave_ts) + ret = regmap_bulk_write(dev->regmap, 0x022, buf, 4); + else + ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4); if (ret) goto err; /* add PID */ buf[0] = (pid >> 8) & 0xff; buf[1] = (pid >> 0) & 0xff; - ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2); + + if (dev->slave_ts) + ret = regmap_bulk_write(dev->regmap, 0x026 + 2 * index, buf, 2); + else + ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2); if (ret) goto err; diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index c1a8a69e9015..9a6d01a9c690 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -44,6 +44,7 @@ struct rtl2832_dev { bool sleeping; struct delayed_work i2c_gate_work; unsigned long filters; /* PID filter */ + bool slave_ts; }; struct rtl2832_reg_entry { diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index a1cd50f331f1..1795abeda658 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -423,7 +423,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct media_devnode *devnode = media_devnode_data(filp); - struct media_device *dev = to_media_device(devnode); + struct media_device *dev = devnode->media_dev; long ret; mutex_lock(&dev->graph_mutex); @@ -495,7 +495,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct media_devnode *devnode = media_devnode_data(filp); - struct media_device *dev = to_media_device(devnode); + struct media_device *dev = devnode->media_dev; long ret; switch (cmd) { @@ -531,7 +531,8 @@ static const struct media_file_operations media_device_fops = { static ssize_t show_model(struct device *cd, struct device_attribute *attr, char *buf) { - struct media_device *mdev = to_media_device(to_media_devnode(cd)); + struct media_devnode *devnode = to_media_devnode(cd); + struct media_device *mdev = devnode->media_dev; return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); } @@ -704,23 +705,35 @@ EXPORT_SYMBOL_GPL(media_device_cleanup); int __must_check __media_device_register(struct media_device *mdev, struct module *owner) { + struct media_devnode *devnode; int ret; + devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); + if (!devnode) + return -ENOMEM; + /* Register the device node. */ - mdev->devnode.fops = &media_device_fops; - mdev->devnode.parent = mdev->dev; - mdev->devnode.release = media_device_release; + mdev->devnode = devnode; + devnode->fops = &media_device_fops; + devnode->parent = mdev->dev; + devnode->release = media_device_release; /* Set version 0 to indicate user-space that the graph is static */ mdev->topology_version = 0; - ret = media_devnode_register(&mdev->devnode, owner); - if (ret < 0) + ret = media_devnode_register(mdev, devnode, owner); + if (ret < 0) { + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; return ret; + } - ret = device_create_file(&mdev->devnode.dev, &dev_attr_model); + ret = device_create_file(&devnode->dev, &dev_attr_model); if (ret < 0) { - media_devnode_unregister(&mdev->devnode); + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; + media_devnode_unregister_prepare(devnode); + media_devnode_unregister(devnode); return ret; } @@ -771,11 +784,14 @@ void media_device_unregister(struct media_device *mdev) mutex_lock(&mdev->graph_mutex); /* Check if mdev was ever registered at all */ - if (!media_devnode_is_registered(&mdev->devnode)) { + if (!media_devnode_is_registered(mdev->devnode)) { mutex_unlock(&mdev->graph_mutex); return; } + /* Clear the devnode register bit to avoid races with media dev open */ + media_devnode_unregister_prepare(mdev->devnode); + /* Remove all entities from the media device */ list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list) __media_device_unregister_entity(entity); @@ -794,9 +810,12 @@ void media_device_unregister(struct media_device *mdev) mutex_unlock(&mdev->graph_mutex); - device_remove_file(&mdev->devnode.dev, &dev_attr_model); - dev_dbg(mdev->dev, "Media device unregistering\n"); - media_devnode_unregister(&mdev->devnode); + dev_dbg(mdev->dev, "Media device unregistered\n"); + + device_remove_file(&mdev->devnode->dev, &dev_attr_model); + media_devnode_unregister(mdev->devnode); + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; } EXPORT_SYMBOL_GPL(media_device_unregister); diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index b66dc9d0766b..f2772ba6f611 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -44,6 +44,7 @@ #include <linux/uaccess.h> #include <media/media-devnode.h> +#include <media/media-device.h> #define MEDIA_NUM_DEVICES 256 #define MEDIA_NAME "media" @@ -59,21 +60,19 @@ static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); /* Called when the last user of the media device exits. */ static void media_devnode_release(struct device *cd) { - struct media_devnode *mdev = to_media_devnode(cd); + struct media_devnode *devnode = to_media_devnode(cd); mutex_lock(&media_devnode_lock); - - /* Delete the cdev on this minor as well */ - cdev_del(&mdev->cdev); - /* Mark device node number as free */ - clear_bit(mdev->minor, media_devnode_nums); - + clear_bit(devnode->minor, media_devnode_nums); mutex_unlock(&media_devnode_lock); /* Release media_devnode and perform other cleanups as needed. */ - if (mdev->release) - mdev->release(mdev); + if (devnode->release) + devnode->release(devnode); + + kfree(devnode); + pr_debug("%s: Media Devnode Deallocated\n", __func__); } static struct bus_type media_bus_type = { @@ -83,37 +82,37 @@ static struct bus_type media_bus_type = { static ssize_t media_read(struct file *filp, char __user *buf, size_t sz, loff_t *off) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (!mdev->fops->read) + if (!devnode->fops->read) return -EINVAL; - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return -EIO; - return mdev->fops->read(filp, buf, sz, off); + return devnode->fops->read(filp, buf, sz, off); } static ssize_t media_write(struct file *filp, const char __user *buf, size_t sz, loff_t *off) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (!mdev->fops->write) + if (!devnode->fops->write) return -EINVAL; - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return -EIO; - return mdev->fops->write(filp, buf, sz, off); + return devnode->fops->write(filp, buf, sz, off); } static unsigned int media_poll(struct file *filp, struct poll_table_struct *poll) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return POLLERR | POLLHUP; - if (!mdev->fops->poll) + if (!devnode->fops->poll) return DEFAULT_POLLMASK; - return mdev->fops->poll(filp, poll); + return devnode->fops->poll(filp, poll); } static long @@ -121,12 +120,12 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, long (*ioctl_func)(struct file *filp, unsigned int cmd, unsigned long arg)) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); if (!ioctl_func) return -ENOTTY; - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return -EIO; return ioctl_func(filp, cmd, arg); @@ -134,9 +133,9 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - return __media_ioctl(filp, cmd, arg, mdev->fops->ioctl); + return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl); } #ifdef CONFIG_COMPAT @@ -144,9 +143,9 @@ static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static long media_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - return __media_ioctl(filp, cmd, arg, mdev->fops->compat_ioctl); + return __media_ioctl(filp, cmd, arg, devnode->fops->compat_ioctl); } #endif /* CONFIG_COMPAT */ @@ -154,7 +153,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd, /* Override for the open function */ static int media_open(struct inode *inode, struct file *filp) { - struct media_devnode *mdev; + struct media_devnode *devnode; int ret; /* Check if the media device is available. This needs to be done with @@ -164,23 +163,23 @@ static int media_open(struct inode *inode, struct file *filp) * a crash. */ mutex_lock(&media_devnode_lock); - mdev = container_of(inode->i_cdev, struct media_devnode, cdev); + devnode = container_of(inode->i_cdev, struct media_devnode, cdev); /* return ENXIO if the media device has been removed already or if it is not registered anymore. */ - if (!media_devnode_is_registered(mdev)) { + if (!media_devnode_is_registered(devnode)) { mutex_unlock(&media_devnode_lock); return -ENXIO; } /* and increase the device refcount */ - get_device(&mdev->dev); + get_device(&devnode->dev); mutex_unlock(&media_devnode_lock); - filp->private_data = mdev; + filp->private_data = devnode; - if (mdev->fops->open) { - ret = mdev->fops->open(filp); + if (devnode->fops->open) { + ret = devnode->fops->open(filp); if (ret) { - put_device(&mdev->dev); + put_device(&devnode->dev); filp->private_data = NULL; return ret; } @@ -192,16 +191,18 @@ static int media_open(struct inode *inode, struct file *filp) /* Override for the release function */ static int media_release(struct inode *inode, struct file *filp) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (mdev->fops->release) - mdev->fops->release(filp); + if (devnode->fops->release) + devnode->fops->release(filp); filp->private_data = NULL; /* decrease the refcount unconditionally since the release() return value is ignored. */ - put_device(&mdev->dev); + put_device(&devnode->dev); + + pr_debug("%s: Media Release\n", __func__); return 0; } @@ -219,7 +220,8 @@ static const struct file_operations media_devnode_fops = { .llseek = no_llseek, }; -int __must_check media_devnode_register(struct media_devnode *mdev, +int __must_check media_devnode_register(struct media_device *mdev, + struct media_devnode *devnode, struct module *owner) { int minor; @@ -231,61 +233,80 @@ int __must_check media_devnode_register(struct media_devnode *mdev, if (minor == MEDIA_NUM_DEVICES) { mutex_unlock(&media_devnode_lock); pr_err("could not get a free minor\n"); + kfree(devnode); return -ENFILE; } set_bit(minor, media_devnode_nums); mutex_unlock(&media_devnode_lock); - mdev->minor = minor; + devnode->minor = minor; + devnode->media_dev = mdev; + + /* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */ + devnode->dev.bus = &media_bus_type; + devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); + devnode->dev.release = media_devnode_release; + if (devnode->parent) + devnode->dev.parent = devnode->parent; + dev_set_name(&devnode->dev, "media%d", devnode->minor); + device_initialize(&devnode->dev); /* Part 2: Initialize and register the character device */ - cdev_init(&mdev->cdev, &media_devnode_fops); - mdev->cdev.owner = owner; + cdev_init(&devnode->cdev, &media_devnode_fops); + devnode->cdev.owner = owner; + devnode->cdev.kobj.parent = &devnode->dev.kobj; - ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1); + ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); if (ret < 0) { pr_err("%s: cdev_add failed\n", __func__); - goto error; + goto cdev_add_error; } - /* Part 3: Register the media device */ - mdev->dev.bus = &media_bus_type; - mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor); - mdev->dev.release = media_devnode_release; - if (mdev->parent) - mdev->dev.parent = mdev->parent; - dev_set_name(&mdev->dev, "media%d", mdev->minor); - ret = device_register(&mdev->dev); + /* Part 3: Add the media device */ + ret = device_add(&devnode->dev); if (ret < 0) { - pr_err("%s: device_register failed\n", __func__); - goto error; + pr_err("%s: device_add failed\n", __func__); + goto device_add_error; } /* Part 4: Activate this minor. The char device can now be used. */ - set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); return 0; -error: +device_add_error: + cdev_del(&devnode->cdev); +cdev_add_error: mutex_lock(&media_devnode_lock); - cdev_del(&mdev->cdev); - clear_bit(mdev->minor, media_devnode_nums); + clear_bit(devnode->minor, media_devnode_nums); + devnode->media_dev = NULL; mutex_unlock(&media_devnode_lock); + put_device(&devnode->dev); return ret; } -void media_devnode_unregister(struct media_devnode *mdev) +void media_devnode_unregister_prepare(struct media_devnode *devnode) { - /* Check if mdev was ever registered at all */ - if (!media_devnode_is_registered(mdev)) + /* Check if devnode was ever registered at all */ + if (!media_devnode_is_registered(devnode)) return; mutex_lock(&media_devnode_lock); - clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + mutex_unlock(&media_devnode_lock); +} + +void media_devnode_unregister(struct media_devnode *devnode) +{ + mutex_lock(&media_devnode_lock); + /* Delete the cdev on this minor as well */ + cdev_del(&devnode->cdev); mutex_unlock(&media_devnode_lock); - device_unregister(&mdev->dev); + device_del(&devnode->dev); + devnode->media_dev = NULL; + put_device(&devnode->dev); } /* diff --git a/drivers/media/pci/netup_unidvb/Kconfig b/drivers/media/pci/netup_unidvb/Kconfig index f277b0b10c2d..0ad37714c7fd 100644 --- a/drivers/media/pci/netup_unidvb/Kconfig +++ b/drivers/media/pci/netup_unidvb/Kconfig @@ -5,8 +5,13 @@ config DVB_NETUP_UNIDVB select VIDEOBUF2_VMALLOC select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT + select DVB_HELENE if MEDIA_SUBDRV_AUTOSELECT select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for NetUP PCI express Universal DVB card. - + help + Say Y when you want to support NetUP Dual Universal DVB card + Card can receive two independent streams in following standards: + DVB-S/S2, T/T2, C/C2 + Two CI slots available for CAM modules. diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb.h b/drivers/media/pci/netup_unidvb/netup_unidvb.h index a67b28111905..39b08ecda1fc 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb.h +++ b/drivers/media/pci/netup_unidvb/netup_unidvb.h @@ -50,6 +50,15 @@ #define NETUP_UNIDVB_IRQ_CAM0 (1 << 11) #define NETUP_UNIDVB_IRQ_CAM1 (1 << 12) +/* NetUP Universal DVB card hardware revisions and it's PCI device id's: + * 1.3 - CXD2841ER demod, ASCOT2E and HORUS3A tuners + * 1.4 - CXD2854ER demod, HELENE tuner +*/ +enum netup_hw_rev { + NETUP_HW_REV_1_3 = 0x18F6, + NETUP_HW_REV_1_4 = 0x18F7 +}; + struct netup_dma { u8 num; spinlock_t lock; @@ -119,6 +128,7 @@ struct netup_unidvb_dev { struct netup_dma dma[2]; struct netup_ci_state ci[2]; struct netup_spi *spi; + enum netup_hw_rev rev; }; int netup_i2c_register(struct netup_unidvb_dev *ndev); diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c index f46ffac66ee9..f535270c2116 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c @@ -147,7 +147,7 @@ static int netup_unidvb_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, { struct netup_ci_state *state = en50221->data; struct netup_unidvb_dev *dev = state->dev; - u8 val = *((u8 __force *)state->membase8_io + addr); + u8 val = *((u8 __force *)state->membase8_config + addr); dev_dbg(&dev->pci_dev->dev, "%s(): addr=0x%x val=0x%x\n", __func__, addr, val); @@ -162,7 +162,7 @@ static int netup_unidvb_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, dev_dbg(&dev->pci_dev->dev, "%s(): addr=0x%x data=0x%x\n", __func__, addr, data); - *((u8 __force *)state->membase8_io + addr) = data; + *((u8 __force *)state->membase8_config + addr) = data; return 0; } diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index 2b667b315913..d278d4e151a0 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -34,6 +34,7 @@ #include "cxd2841er.h" #include "horus3a.h" #include "ascot2e.h" +#include "helene.h" #include "lnbh25.h" static int spi_enable; @@ -120,7 +121,8 @@ static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc); static void netup_unidvb_queue_cleanup(struct netup_dma *dma); static struct cxd2841er_config demod_config = { - .i2c_addr = 0xc8 + .i2c_addr = 0xc8, + .xtal = SONY_XTAL_24000 }; static struct horus3a_config horus3a_conf = { @@ -134,6 +136,12 @@ static struct ascot2e_config ascot2e_conf = { .set_tuner_callback = netup_unidvb_tuner_ctrl }; +static struct helene_config helene_conf = { + .i2c_address = 0xc0, + .xtal = SONY_HELENE_XTAL_24000, + .set_tuner_callback = netup_unidvb_tuner_ctrl +}; + static struct lnbh25_config lnbh25_conf = { .i2c_address = 0x10, .data2_config = LNBH25_TEN | LNBH25_EXTM @@ -152,6 +160,11 @@ static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc) __func__, dma->num, is_dvb_tc); reg = readb(ndev->bmmio0 + GPIO_REG_IO); mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL; + + /* inverted tuner control in hw rev. 1.4 */ + if (ndev->rev == NETUP_HW_REV_1_4) + is_dvb_tc = !is_dvb_tc; + if (!is_dvb_tc) reg |= mask; else @@ -372,7 +385,15 @@ static int netup_unidvb_queue_init(struct netup_dma *dma, static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, int num) { - struct vb2_dvb_frontend *fe0, *fe1, *fe2; + int fe_count = 2; + int i = 0; + struct vb2_dvb_frontend *fes[2]; + u8 fe_name[32]; + + if (ndev->rev == NETUP_HW_REV_1_3) + demod_config.xtal = SONY_XTAL_20500; + else + demod_config.xtal = SONY_XTAL_24000; if (num < 0 || num > 1) { dev_dbg(&ndev->pci_dev->dev, @@ -381,84 +402,96 @@ static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, } mutex_init(&ndev->frontends[num].lock); INIT_LIST_HEAD(&ndev->frontends[num].felist); - if (vb2_dvb_alloc_frontend(&ndev->frontends[num], 1) == NULL || - vb2_dvb_alloc_frontend( - &ndev->frontends[num], 2) == NULL || - vb2_dvb_alloc_frontend( - &ndev->frontends[num], 3) == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to allocate vb2_dvb_frontend\n", - __func__); - return -ENOMEM; + + for (i = 0; i < fe_count; i++) { + if (vb2_dvb_alloc_frontend(&ndev->frontends[num], i+1) + == NULL) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to allocate vb2_dvb_frontend\n", + __func__); + return -ENOMEM; + } } - fe0 = vb2_dvb_get_frontend(&ndev->frontends[num], 1); - fe1 = vb2_dvb_get_frontend(&ndev->frontends[num], 2); - fe2 = vb2_dvb_get_frontend(&ndev->frontends[num], 3); - if (fe0 == NULL || fe1 == NULL || fe2 == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): frontends has not been allocated\n", __func__); - return -EINVAL; + + for (i = 0; i < fe_count; i++) { + fes[i] = vb2_dvb_get_frontend(&ndev->frontends[num], i+1); + if (fes[i] == NULL) { + dev_err(&ndev->pci_dev->dev, + "%s(): frontends has not been allocated\n", + __func__); + return -EINVAL; + } + } + + for (i = 0; i < fe_count; i++) { + netup_unidvb_queue_init(&ndev->dma[num], &fes[i]->dvb.dvbq); + snprintf(fe_name, sizeof(fe_name), "netup_fe%d", i); + fes[i]->dvb.name = fe_name; } - netup_unidvb_queue_init(&ndev->dma[num], &fe0->dvb.dvbq); - netup_unidvb_queue_init(&ndev->dma[num], &fe1->dvb.dvbq); - netup_unidvb_queue_init(&ndev->dma[num], &fe2->dvb.dvbq); - fe0->dvb.name = "netup_fe0"; - fe1->dvb.name = "netup_fe1"; - fe2->dvb.name = "netup_fe2"; - fe0->dvb.frontend = dvb_attach(cxd2841er_attach_s, + + fes[0]->dvb.frontend = dvb_attach(cxd2841er_attach_s, &demod_config, &ndev->i2c[num].adap); - if (fe0->dvb.frontend == NULL) { + if (fes[0]->dvb.frontend == NULL) { dev_dbg(&ndev->pci_dev->dev, "%s(): unable to attach DVB-S/S2 frontend\n", __func__); goto frontend_detach; } - horus3a_conf.set_tuner_priv = &ndev->dma[num]; - if (!dvb_attach(horus3a_attach, fe0->dvb.frontend, - &horus3a_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-S/S2 tuner frontend\n", - __func__); - goto frontend_detach; + + if (ndev->rev == NETUP_HW_REV_1_3) { + horus3a_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(horus3a_attach, fes[0]->dvb.frontend, + &horus3a_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach HORUS3A DVB-S/S2 tuner frontend\n", + __func__); + goto frontend_detach; + } + } else { + helene_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(helene_attach_s, fes[0]->dvb.frontend, + &helene_conf, &ndev->i2c[num].adap)) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to attach HELENE DVB-S/S2 tuner frontend\n", + __func__); + goto frontend_detach; + } } - if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend, + + if (!dvb_attach(lnbh25_attach, fes[0]->dvb.frontend, &lnbh25_conf, &ndev->i2c[num].adap)) { dev_dbg(&ndev->pci_dev->dev, "%s(): unable to attach SEC frontend\n", __func__); goto frontend_detach; } + /* DVB-T/T2 frontend */ - fe1->dvb.frontend = dvb_attach(cxd2841er_attach_t, + fes[1]->dvb.frontend = dvb_attach(cxd2841er_attach_t_c, &demod_config, &ndev->i2c[num].adap); - if (fe1->dvb.frontend == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T frontend\n", __func__); - goto frontend_detach; - } - fe1->dvb.frontend->id = 1; - ascot2e_conf.set_tuner_priv = &ndev->dma[num]; - if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend, - &ascot2e_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T tuner frontend\n", - __func__); - goto frontend_detach; - } - /* DVB-C/C2 frontend */ - fe2->dvb.frontend = dvb_attach(cxd2841er_attach_c, - &demod_config, &ndev->i2c[num].adap); - if (fe2->dvb.frontend == NULL) { + if (fes[1]->dvb.frontend == NULL) { dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-C frontend\n", __func__); + "%s(): unable to attach Ter frontend\n", __func__); goto frontend_detach; } - fe2->dvb.frontend->id = 2; - if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend, - &ascot2e_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T/C tuner frontend\n", - __func__); - goto frontend_detach; + fes[1]->dvb.frontend->id = 1; + if (ndev->rev == NETUP_HW_REV_1_3) { + ascot2e_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(ascot2e_attach, fes[1]->dvb.frontend, + &ascot2e_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach Ter tuner frontend\n", + __func__); + goto frontend_detach; + } + } else { + helene_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(helene_attach, fes[1]->dvb.frontend, + &helene_conf, &ndev->i2c[num].adap)) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to attach HELENE Ter tuner frontend\n", + __func__); + goto frontend_detach; + } } if (vb2_dvb_register_bus(&ndev->frontends[num], @@ -730,7 +763,7 @@ static int netup_unidvb_request_mmio(struct pci_dev *pci_dev) static int netup_unidvb_request_modules(struct device *dev) { static const char * const modules[] = { - "lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL + "lnbh25", "ascot2e", "horus3a", "cxd2841er", "helene", NULL }; const char * const *curr_mod = modules; int err; @@ -774,6 +807,16 @@ static int netup_unidvb_initdev(struct pci_dev *pci_dev, if (!ndev) goto dev_alloc_err; + /* detect hardware revision */ + if (pci_dev->device == NETUP_HW_REV_1_3) + ndev->rev = NETUP_HW_REV_1_3; + else + ndev->rev = NETUP_HW_REV_1_4; + + dev_info(&pci_dev->dev, + "%s(): board (0x%x) hardware revision 0x%x\n", + __func__, pci_dev->device, ndev->rev); + ndev->old_fw = old_firmware; ndev->wq = create_singlethread_workqueue(NETUP_UNIDVB_NAME); if (!ndev->wq) { @@ -972,7 +1015,8 @@ static void netup_unidvb_finidev(struct pci_dev *pci_dev) static struct pci_device_id netup_unidvb_pci_tbl[] = { - { PCI_DEVICE(0x1b55, 0x18f6) }, + { PCI_DEVICE(0x1b55, 0x18f6) }, /* hw rev. 1.3 */ + { PCI_DEVICE(0x1b55, 0x18f7) }, /* hw rev. 1.4 */ { 0, } }; MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 84e041c0a70e..9d0a3ffe36d2 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -110,6 +110,7 @@ source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" +source "drivers/media/platform/rcar-vin/Kconfig" config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" @@ -247,10 +248,24 @@ config VIDEO_RENESAS_JPU To compile this driver as a module, choose M here: the module will be called rcar_jpu. +config VIDEO_RENESAS_FCP + tristate "Renesas Frame Compression Processor" + depends on ARCH_RENESAS || COMPILE_TEST + depends on OF + ---help--- + This is a driver for the Renesas Frame Compression Processor (FCP). + The FCP is a companion module of video processing modules in the + Renesas R-Car Gen3 SoCs. It handles memory access for the codec, + VSP and FDP modules. + + To compile this driver as a module, choose M here: the module + will be called rcar-fcp. + config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA depends on (ARCH_RENESAS && OF) || COMPILE_TEST + depends on !ARM64 || VIDEO_RENESAS_FCP select VIDEOBUF2_DMA_CONTIG ---help--- This is a V4L2 driver for the Renesas VSP1 video processing engine. diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index bbb7bd1eb268..c83800a74a2d 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o obj-$(CONFIG_SOC_CAMERA) += soc_camera/ +obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ @@ -55,4 +56,6 @@ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ obj-$(CONFIG_VIDEO_XILINX) += xilinx/ +obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/ + ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index c04973669a47..c9d2009c2476 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1124,6 +1124,7 @@ static int gsc_probe(struct platform_device *pdev) goto err_m2m; /* Initialize continious memory allocator */ + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); gsc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(gsc->alloc_ctx)) { ret = PTR_ERR(gsc->alloc_ctx); @@ -1153,6 +1154,7 @@ static int gsc_remove(struct platform_device *pdev) v4l2_device_unregister(&gsc->v4l2_dev); vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); pm_runtime_disable(&pdev->dev); gsc_clk_put(gsc); diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index b1c1cea82a27..368f44f24d4c 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -1019,6 +1019,7 @@ static int fimc_probe(struct platform_device *pdev) } /* Initialize contiguous memory allocator */ + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); @@ -1124,6 +1125,7 @@ static int fimc_remove(struct platform_device *pdev) fimc_unregister_capture_subdev(fimc); vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); clk_disable(fimc->clock[CLK_BUS]); fimc_clk_put(fimc); diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 979c388ebf60..bd98b56318b7 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -847,6 +847,7 @@ static int fimc_is_probe(struct platform_device *pdev) if (ret < 0) goto err_pm; + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); is->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(is->alloc_ctx)) { ret = PTR_ERR(is->alloc_ctx); @@ -940,6 +941,7 @@ static int fimc_is_remove(struct platform_device *pdev) free_irq(is->irq, is); fimc_is_unregister_subdevs(is); vb2_dma_contig_cleanup_ctx(is->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(dev); fimc_is_put_clocks(is); fimc_is_debugfs_remove(is); release_firmware(is->fw.f_w); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index dc1b929f7a33..27cb620cb714 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1551,6 +1551,7 @@ static int fimc_lite_probe(struct platform_device *pdev) goto err_sd; } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); @@ -1652,6 +1653,7 @@ static int fimc_lite_remove(struct platform_device *pdev) pm_runtime_set_suspended(dev); fimc_lite_unregister_capture_subdev(fimc); vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(dev); fimc_lite_clk_put(fimc); dev_info(dev, "Driver unloaded\n"); diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c new file mode 100644 index 000000000000..6a7bcc3028b1 --- /dev/null +++ b/drivers/media/platform/rcar-fcp.c @@ -0,0 +1,181 @@ +/* + * rcar-fcp.c -- R-Car Frame Compression Processor Driver + * + * Copyright (C) 2016 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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. + */ + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include <media/rcar-fcp.h> + +struct rcar_fcp_device { + struct list_head list; + struct device *dev; +}; + +static LIST_HEAD(fcp_devices); +static DEFINE_MUTEX(fcp_lock); + +/* ----------------------------------------------------------------------------- + * Public API + */ + +/** + * rcar_fcp_get - Find and acquire a reference to an FCP instance + * @np: Device node of the FCP instance + * + * Search the list of registered FCP instances for the instance corresponding to + * the given device node. + * + * Return a pointer to the FCP instance, or an ERR_PTR if the instance can't be + * found. + */ +struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np) +{ + struct rcar_fcp_device *fcp; + + mutex_lock(&fcp_lock); + + list_for_each_entry(fcp, &fcp_devices, list) { + if (fcp->dev->of_node != np) + continue; + + /* + * Make sure the module won't be unloaded behind our back. This + * is a poor man's safety net, the module should really not be + * unloaded while FCP users can be active. + */ + if (!try_module_get(fcp->dev->driver->owner)) + fcp = NULL; + + goto done; + } + + fcp = ERR_PTR(-EPROBE_DEFER); + +done: + mutex_unlock(&fcp_lock); + return fcp; +} +EXPORT_SYMBOL_GPL(rcar_fcp_get); + +/** + * rcar_fcp_put - Release a reference to an FCP instance + * @fcp: The FCP instance + * + * Release the FCP instance acquired by a call to rcar_fcp_get(). + */ +void rcar_fcp_put(struct rcar_fcp_device *fcp) +{ + if (fcp) + module_put(fcp->dev->driver->owner); +} +EXPORT_SYMBOL_GPL(rcar_fcp_put); + +/** + * rcar_fcp_enable - Enable an FCP + * @fcp: The FCP instance + * + * Before any memory access through an FCP is performed by a module, the FCP + * must be enabled by a call to this function. The enable calls are reference + * counted, each successful call must be followed by one rcar_fcp_disable() + * call when no more memory transfer can occur through the FCP. + * + * Return 0 on success or a negative error code if an error occurs. The enable + * reference count isn't increased when this function returns an error. + */ +int rcar_fcp_enable(struct rcar_fcp_device *fcp) +{ + if (!fcp) + return 0; + + return pm_runtime_get_sync(fcp->dev); +} +EXPORT_SYMBOL_GPL(rcar_fcp_enable); + +/** + * rcar_fcp_disable - Disable an FCP + * @fcp: The FCP instance + * + * This function is the counterpart of rcar_fcp_enable(). As enable calls are + * reference counted a disable call may not disable the FCP synchronously. + */ +void rcar_fcp_disable(struct rcar_fcp_device *fcp) +{ + if (fcp) + pm_runtime_put(fcp->dev); +} +EXPORT_SYMBOL_GPL(rcar_fcp_disable); + +/* ----------------------------------------------------------------------------- + * Platform Driver + */ + +static int rcar_fcp_probe(struct platform_device *pdev) +{ + struct rcar_fcp_device *fcp; + + fcp = devm_kzalloc(&pdev->dev, sizeof(*fcp), GFP_KERNEL); + if (fcp == NULL) + return -ENOMEM; + + fcp->dev = &pdev->dev; + + pm_runtime_enable(&pdev->dev); + + mutex_lock(&fcp_lock); + list_add_tail(&fcp->list, &fcp_devices); + mutex_unlock(&fcp_lock); + + platform_set_drvdata(pdev, fcp); + + return 0; +} + +static int rcar_fcp_remove(struct platform_device *pdev) +{ + struct rcar_fcp_device *fcp = platform_get_drvdata(pdev); + + mutex_lock(&fcp_lock); + list_del(&fcp->list); + mutex_unlock(&fcp_lock); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id rcar_fcp_of_match[] = { + { .compatible = "renesas,fcpv" }, + { }, +}; + +static struct platform_driver rcar_fcp_platform_driver = { + .probe = rcar_fcp_probe, + .remove = rcar_fcp_remove, + .driver = { + .name = "rcar-fcp", + .of_match_table = rcar_fcp_of_match, + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(rcar_fcp_platform_driver); + +MODULE_ALIAS("rcar-fcp"); +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_DESCRIPTION("Renesas FCP Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig new file mode 100644 index 000000000000..b2ff2d4e8bb1 --- /dev/null +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -0,0 +1,11 @@ +config VIDEO_RCAR_VIN + tristate "R-Car Video Input (VIN) Driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA + depends on ARCH_RENESAS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + ---help--- + Support for Renesas R-Car Video Input (VIN) driver. + Supports R-Car Gen2 SoCs. + + To compile this driver as a module, choose M here: the + module will be called rcar-vin. diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/rcar-vin/Makefile new file mode 100644 index 000000000000..48c5632c21dc --- /dev/null +++ b/drivers/media/platform/rcar-vin/Makefile @@ -0,0 +1,3 @@ +rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o + +obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin.o diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c new file mode 100644 index 000000000000..4b2007b73463 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -0,0 +1,334 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * 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. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include <media/v4l2-of.h> + +#include "rcar-vin.h" + +/* ----------------------------------------------------------------------------- + * Async notifier + */ + +#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier) + +static int rvin_mbus_supported(struct rvin_dev *vin) +{ + struct v4l2_subdev *sd; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + sd = vin_to_source(vin); + + code.index = 0; + while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { + code.index++; + switch (code.code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_RGB888_1X24: + vin->source.code = code.code; + vin_dbg(vin, "Found supported media bus format: %d\n", + vin->source.code); + return true; + default: + break; + } + } + + return false; +} + +static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + int ret; + + ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); + if (ret < 0) { + vin_err(vin, "Failed to register subdev nodes\n"); + return ret; + } + + if (!rvin_mbus_supported(vin)) { + vin_err(vin, "No supported mediabus format found\n"); + return -EINVAL; + } + + return rvin_v4l2_probe(vin); +} + +static void rvin_graph_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + + rvin_v4l2_remove(vin); +} + +static int rvin_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + + vin_dbg(vin, "subdev %s bound\n", subdev->name); + + vin->entity.entity = &subdev->entity; + vin->entity.subdev = subdev; + + return 0; +} + +static int rvin_graph_parse(struct rvin_dev *vin, + struct device_node *node) +{ + struct device_node *remote; + struct device_node *ep = NULL; + struct device_node *next; + int ret = 0; + + while (1) { + next = of_graph_get_next_endpoint(node, ep); + if (!next) + break; + + of_node_put(ep); + ep = next; + + remote = of_graph_get_remote_port_parent(ep); + if (!remote) { + ret = -EINVAL; + break; + } + + /* Skip entities that we have already processed. */ + if (remote == vin->dev->of_node) { + of_node_put(remote); + continue; + } + + /* Remote node to connect */ + if (!vin->entity.node) { + vin->entity.node = remote; + vin->entity.asd.match_type = V4L2_ASYNC_MATCH_OF; + vin->entity.asd.match.of.node = remote; + ret++; + } + } + + of_node_put(ep); + + return ret; +} + +static int rvin_graph_init(struct rvin_dev *vin) +{ + struct v4l2_async_subdev **subdevs = NULL; + int ret; + + /* Parse the graph to extract a list of subdevice DT nodes. */ + ret = rvin_graph_parse(vin, vin->dev->of_node); + if (ret < 0) { + vin_err(vin, "Graph parsing failed\n"); + goto done; + } + + if (!ret) { + vin_err(vin, "No subdev found in graph\n"); + goto done; + } + + if (ret != 1) { + vin_err(vin, "More then one subdev found in graph\n"); + goto done; + } + + /* Register the subdevices notifier. */ + subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL); + if (subdevs == NULL) { + ret = -ENOMEM; + goto done; + } + + subdevs[0] = &vin->entity.asd; + + vin->notifier.subdevs = subdevs; + vin->notifier.num_subdevs = 1; + vin->notifier.bound = rvin_graph_notify_bound; + vin->notifier.unbind = rvin_graph_notify_unbind; + vin->notifier.complete = rvin_graph_notify_complete; + + ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); + if (ret < 0) { + vin_err(vin, "Notifier registration failed\n"); + goto done; + } + + ret = 0; + +done: + if (ret < 0) { + v4l2_async_notifier_unregister(&vin->notifier); + of_node_put(vin->entity.node); + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static const struct of_device_id rvin_of_id_table[] = { + { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, + { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, + { }, +}; +MODULE_DEVICE_TABLE(of, rvin_of_id_table); + +static int rvin_parse_dt(struct rvin_dev *vin) +{ + const struct of_device_id *match; + struct v4l2_of_endpoint ep; + struct device_node *np; + int ret; + + match = of_match_device(of_match_ptr(rvin_of_id_table), vin->dev); + if (!match) + return -ENODEV; + + vin->chip = (enum chip_id)match->data; + + np = of_graph_get_next_endpoint(vin->dev->of_node, NULL); + if (!np) { + vin_err(vin, "Could not find endpoint\n"); + return -EINVAL; + } + + ret = v4l2_of_parse_endpoint(np, &ep); + if (ret) { + vin_err(vin, "Could not parse endpoint\n"); + return ret; + } + + of_node_put(np); + + vin->mbus_cfg.type = ep.bus_type; + + switch (vin->mbus_cfg.type) { + case V4L2_MBUS_PARALLEL: + vin->mbus_cfg.flags = ep.bus.parallel.flags; + break; + case V4L2_MBUS_BT656: + vin->mbus_cfg.flags = 0; + break; + default: + vin_err(vin, "Unknown media bus type\n"); + return -EINVAL; + } + + return 0; +} + +static int rcar_vin_probe(struct platform_device *pdev) +{ + struct rvin_dev *vin; + struct resource *mem; + int irq, ret; + + vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL); + if (!vin) + return -ENOMEM; + + vin->dev = &pdev->dev; + + ret = rvin_parse_dt(vin); + if (ret) + return ret; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem == NULL) + return -EINVAL; + + vin->base = devm_ioremap_resource(vin->dev, mem); + if (IS_ERR(vin->base)) + return PTR_ERR(vin->base); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return ret; + + ret = rvin_dma_probe(vin, irq); + if (ret) + return ret; + + ret = rvin_graph_init(vin); + if (ret < 0) + goto error; + + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + + platform_set_drvdata(pdev, vin); + + return 0; +error: + rvin_dma_remove(vin); + + return ret; +} + +static int rcar_vin_remove(struct platform_device *pdev) +{ + struct rvin_dev *vin = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + v4l2_async_notifier_unregister(&vin->notifier); + + rvin_dma_remove(vin); + + return 0; +} + +static struct platform_driver rcar_vin_driver = { + .driver = { + .name = "rcar-vin", + .of_match_table = rvin_of_id_table, + }, + .probe = rcar_vin_probe, + .remove = rcar_vin_remove, +}; + +module_platform_driver(rcar_vin_driver); + +MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>"); +MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c new file mode 100644 index 000000000000..dad3b031f778 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -0,0 +1,1196 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * 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. + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> + +#include <media/videobuf2-dma-contig.h> + +#include "rcar-vin.h" + +/* ----------------------------------------------------------------------------- + * HW Functions + */ + +/* Register offsets for R-Car VIN */ +#define VNMC_REG 0x00 /* Video n Main Control Register */ +#define VNMS_REG 0x04 /* Video n Module Status Register */ +#define VNFC_REG 0x08 /* Video n Frame Capture Register */ +#define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */ +#define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */ +#define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */ +#define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */ +#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ +#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ +#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ +#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ +#define VNIS_REG 0x2C /* Video n Image Stride Register */ +#define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */ +#define VNIE_REG 0x40 /* Video n Interrupt Enable Register */ +#define VNINTS_REG 0x44 /* Video n Interrupt Status Register */ +#define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */ +#define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */ +#define VNYS_REG 0x50 /* Video n Y Scale Register */ +#define VNXS_REG 0x54 /* Video n X Scale Register */ +#define VNDMR_REG 0x58 /* Video n Data Mode Register */ +#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */ +#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */ +#define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */ +#define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */ +#define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */ +#define VNC2A_REG 0x90 /* Video n Coefficient Set C2A Register */ +#define VNC2B_REG 0x94 /* Video n Coefficient Set C2B Register */ +#define VNC2C_REG 0x98 /* Video n Coefficient Set C2C Register */ +#define VNC3A_REG 0xA0 /* Video n Coefficient Set C3A Register */ +#define VNC3B_REG 0xA4 /* Video n Coefficient Set C3B Register */ +#define VNC3C_REG 0xA8 /* Video n Coefficient Set C3C Register */ +#define VNC4A_REG 0xB0 /* Video n Coefficient Set C4A Register */ +#define VNC4B_REG 0xB4 /* Video n Coefficient Set C4B Register */ +#define VNC4C_REG 0xB8 /* Video n Coefficient Set C4C Register */ +#define VNC5A_REG 0xC0 /* Video n Coefficient Set C5A Register */ +#define VNC5B_REG 0xC4 /* Video n Coefficient Set C5B Register */ +#define VNC5C_REG 0xC8 /* Video n Coefficient Set C5C Register */ +#define VNC6A_REG 0xD0 /* Video n Coefficient Set C6A Register */ +#define VNC6B_REG 0xD4 /* Video n Coefficient Set C6B Register */ +#define VNC6C_REG 0xD8 /* Video n Coefficient Set C6C Register */ +#define VNC7A_REG 0xE0 /* Video n Coefficient Set C7A Register */ +#define VNC7B_REG 0xE4 /* Video n Coefficient Set C7B Register */ +#define VNC7C_REG 0xE8 /* Video n Coefficient Set C7C Register */ +#define VNC8A_REG 0xF0 /* Video n Coefficient Set C8A Register */ +#define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */ +#define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */ + + +/* Register bit fields for R-Car VIN */ +/* Video n Main Control Register bits */ +#define VNMC_FOC (1 << 21) +#define VNMC_YCAL (1 << 19) +#define VNMC_INF_YUV8_BT656 (0 << 16) +#define VNMC_INF_YUV8_BT601 (1 << 16) +#define VNMC_INF_YUV10_BT656 (2 << 16) +#define VNMC_INF_YUV10_BT601 (3 << 16) +#define VNMC_INF_YUV16 (5 << 16) +#define VNMC_INF_RGB888 (6 << 16) +#define VNMC_VUP (1 << 10) +#define VNMC_IM_ODD (0 << 3) +#define VNMC_IM_ODD_EVEN (1 << 3) +#define VNMC_IM_EVEN (2 << 3) +#define VNMC_IM_FULL (3 << 3) +#define VNMC_BPS (1 << 1) +#define VNMC_ME (1 << 0) + +/* Video n Module Status Register bits */ +#define VNMS_FBS_MASK (3 << 3) +#define VNMS_FBS_SHIFT 3 +#define VNMS_AV (1 << 1) +#define VNMS_CA (1 << 0) + +/* Video n Frame Capture Register bits */ +#define VNFC_C_FRAME (1 << 1) +#define VNFC_S_FRAME (1 << 0) + +/* Video n Interrupt Enable Register bits */ +#define VNIE_FIE (1 << 4) +#define VNIE_EFE (1 << 1) + +/* Video n Data Mode Register bits */ +#define VNDMR_EXRGB (1 << 8) +#define VNDMR_BPSM (1 << 4) +#define VNDMR_DTMD_YCSEP (1 << 1) +#define VNDMR_DTMD_ARGB1555 (1 << 0) + +/* Video n Data Mode Register 2 bits */ +#define VNDMR2_VPS (1 << 30) +#define VNDMR2_HPS (1 << 29) +#define VNDMR2_FTEV (1 << 17) +#define VNDMR2_VLV(n) ((n & 0xf) << 12) + +static void rvin_write(struct rvin_dev *vin, u32 value, u32 offset) +{ + iowrite32(value, vin->base + offset); +} + +static u32 rvin_read(struct rvin_dev *vin, u32 offset) +{ + return ioread32(vin->base + offset); +} + +static int rvin_setup(struct rvin_dev *vin) +{ + u32 vnmc, dmr, dmr2, interrupts; + bool progressive = false, output_is_yuv = false, input_is_yuv = false; + + switch (vin->format.field) { + case V4L2_FIELD_TOP: + vnmc = VNMC_IM_ODD; + break; + case V4L2_FIELD_BOTTOM: + vnmc = VNMC_IM_EVEN; + break; + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + vnmc = VNMC_IM_FULL; + break; + case V4L2_FIELD_INTERLACED_BT: + vnmc = VNMC_IM_FULL | VNMC_FOC; + break; + case V4L2_FIELD_NONE: + if (vin->continuous) { + vnmc = VNMC_IM_ODD_EVEN; + progressive = true; + } else { + vnmc = VNMC_IM_ODD; + } + break; + default: + vnmc = VNMC_IM_ODD; + break; + } + + /* + * Input interface + */ + switch (vin->source.code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_YUYV8_2X8: + /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ + vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + vnmc |= VNMC_INF_RGB888; + break; + case MEDIA_BUS_FMT_YUYV10_2X10: + /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ + vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + input_is_yuv = true; + break; + default: + break; + } + + /* Enable VSYNC Field Toogle mode after one VSYNC input */ + dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); + + /* Hsync Signal Polarity Select */ + if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_HPS; + + /* Vsync Signal Polarity Select */ + if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_VPS; + + /* + * Output format + */ + switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_NV16: + rvin_write(vin, + ALIGN(vin->format.width * vin->format.height, 0x80), + VNUVAOF_REG); + dmr = VNDMR_DTMD_YCSEP; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_YUYV: + dmr = VNDMR_BPSM; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_UYVY: + dmr = 0; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_XRGB555: + dmr = VNDMR_DTMD_ARGB1555; + break; + case V4L2_PIX_FMT_RGB565: + dmr = 0; + break; + case V4L2_PIX_FMT_XBGR32: + if (vin->chip == RCAR_GEN2 || vin->chip == RCAR_H1) { + dmr = VNDMR_EXRGB; + break; + } + /* fall through */ + default: + vin_err(vin, "Invalid pixelformat (0x%x)\n", + vin->format.pixelformat); + return -EINVAL; + } + + /* Always update on field change */ + vnmc |= VNMC_VUP; + + /* If input and output use the same colorspace, use bypass mode */ + if (input_is_yuv == output_is_yuv) + vnmc |= VNMC_BPS; + + /* Progressive or interlaced mode */ + interrupts = progressive ? VNIE_FIE : VNIE_EFE; + + /* Ack interrupts */ + rvin_write(vin, interrupts, VNINTS_REG); + /* Enable interrupts */ + rvin_write(vin, interrupts, VNIE_REG); + /* Start capturing */ + rvin_write(vin, dmr, VNDMR_REG); + rvin_write(vin, dmr2, VNDMR2_REG); + + /* Enable module */ + rvin_write(vin, vnmc | VNMC_ME, VNMC_REG); + + return 0; +} + +static void rvin_capture_on(struct rvin_dev *vin) +{ + vin_dbg(vin, "Capture on in %s mode\n", + vin->continuous ? "continuous" : "single"); + + if (vin->continuous) + /* Continuous Frame Capture Mode */ + rvin_write(vin, VNFC_C_FRAME, VNFC_REG); + else + /* Single Frame Capture Mode */ + rvin_write(vin, VNFC_S_FRAME, VNFC_REG); +} + +static void rvin_capture_off(struct rvin_dev *vin) +{ + /* Set continuous & single transfer off */ + rvin_write(vin, 0, VNFC_REG); +} + +static int rvin_capture_start(struct rvin_dev *vin) +{ + int ret; + + rvin_crop_scale_comp(vin); + + ret = rvin_setup(vin); + if (ret) + return ret; + + rvin_capture_on(vin); + + return 0; +} + +static void rvin_capture_stop(struct rvin_dev *vin) +{ + rvin_capture_off(vin); + + /* Disable module */ + rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG); +} + +static void rvin_disable_interrupts(struct rvin_dev *vin) +{ + rvin_write(vin, 0, VNIE_REG); +} + +static u32 rvin_get_interrupt_status(struct rvin_dev *vin) +{ + return rvin_read(vin, VNINTS_REG); +} + +static void rvin_ack_interrupt(struct rvin_dev *vin) +{ + rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG); +} + +static bool rvin_capture_active(struct rvin_dev *vin) +{ + return rvin_read(vin, VNMS_REG) & VNMS_CA; +} + +static int rvin_get_active_slot(struct rvin_dev *vin) +{ + if (vin->continuous) + return (rvin_read(vin, VNMS_REG) & VNMS_FBS_MASK) + >> VNMS_FBS_SHIFT; + + return 0; +} + +static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) +{ + const struct rvin_video_format *fmt; + int offsetx, offsety; + dma_addr_t offset; + + fmt = rvin_format_from_pixel(vin->format.pixelformat); + + /* + * There is no HW support for composition do the beast we can + * by modifying the buffer offset + */ + offsetx = vin->compose.left * fmt->bpp; + offsety = vin->compose.top * vin->format.bytesperline; + offset = addr + offsetx + offsety; + + /* + * The address needs to be 128 bytes aligned. Driver should never accept + * settings that do not satisfy this in the first place... + */ + if (WARN_ON((offsetx | offsety | offset) & HW_BUFFER_MASK)) + return; + + rvin_write(vin, offset, VNMB_REG(slot)); +} + +/* ----------------------------------------------------------------------------- + * Crop and Scaling Gen2 + */ + +struct vin_coeff { + unsigned short xs_value; + u32 coeff_set[24]; +}; + +static const struct vin_coeff vin_coeff_set[] = { + { 0x0000, { + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000 }, + }, + { 0x1000, { + 0x000fa400, 0x000fa400, 0x09625902, + 0x000003f8, 0x00000403, 0x3de0d9f0, + 0x001fffed, 0x00000804, 0x3cc1f9c3, + 0x001003de, 0x00000c01, 0x3cb34d7f, + 0x002003d2, 0x00000c00, 0x3d24a92d, + 0x00200bca, 0x00000bff, 0x3df600d2, + 0x002013cc, 0x000007ff, 0x3ed70c7e, + 0x00100fde, 0x00000000, 0x3f87c036 }, + }, + { 0x1200, { + 0x002ffff1, 0x002ffff1, 0x02a0a9c8, + 0x002003e7, 0x001ffffa, 0x000185bc, + 0x002007dc, 0x000003ff, 0x3e52859c, + 0x00200bd4, 0x00000002, 0x3d53996b, + 0x00100fd0, 0x00000403, 0x3d04ad2d, + 0x00000bd5, 0x00000403, 0x3d35ace7, + 0x3ff003e4, 0x00000801, 0x3dc674a1, + 0x3fffe800, 0x00000800, 0x3e76f461 }, + }, + { 0x1400, { + 0x00100be3, 0x00100be3, 0x04d1359a, + 0x00000fdb, 0x002003ed, 0x0211fd93, + 0x00000fd6, 0x002003f4, 0x0002d97b, + 0x000007d6, 0x002ffffb, 0x3e93b956, + 0x3ff003da, 0x001003ff, 0x3db49926, + 0x3fffefe9, 0x00100001, 0x3d655cee, + 0x3fffd400, 0x00000003, 0x3d65f4b6, + 0x000fb421, 0x00000402, 0x3dc6547e }, + }, + { 0x1600, { + 0x00000bdd, 0x00000bdd, 0x06519578, + 0x3ff007da, 0x00000be3, 0x03c24973, + 0x3ff003d9, 0x00000be9, 0x01b30d5f, + 0x3ffff7df, 0x001003f1, 0x0003c542, + 0x000fdfec, 0x001003f7, 0x3ec4711d, + 0x000fc400, 0x002ffffd, 0x3df504f1, + 0x001fa81a, 0x002ffc00, 0x3d957cc2, + 0x002f8c3c, 0x00100000, 0x3db5c891 }, + }, + { 0x1800, { + 0x3ff003dc, 0x3ff003dc, 0x0791e558, + 0x000ff7dd, 0x3ff007de, 0x05328554, + 0x000fe7e3, 0x3ff00be2, 0x03232546, + 0x000fd7ee, 0x000007e9, 0x0143bd30, + 0x001fb800, 0x000007ee, 0x00044511, + 0x002fa015, 0x000007f4, 0x3ef4bcee, + 0x002f8832, 0x001003f9, 0x3e4514c7, + 0x001f7853, 0x001003fd, 0x3de54c9f }, + }, + { 0x1a00, { + 0x000fefe0, 0x000fefe0, 0x08721d3c, + 0x001fdbe7, 0x000ffbde, 0x0652a139, + 0x001fcbf0, 0x000003df, 0x0463292e, + 0x002fb3ff, 0x3ff007e3, 0x0293a91d, + 0x002f9c12, 0x3ff00be7, 0x01241905, + 0x001f8c29, 0x000007ed, 0x3fe470eb, + 0x000f7c46, 0x000007f2, 0x3f04b8ca, + 0x3fef7865, 0x000007f6, 0x3e74e4a8 }, + }, + { 0x1c00, { + 0x001fd3e9, 0x001fd3e9, 0x08f23d26, + 0x002fbff3, 0x001fe3e4, 0x0712ad23, + 0x002fa800, 0x000ff3e0, 0x05631d1b, + 0x001f9810, 0x000ffbe1, 0x03b3890d, + 0x000f8c23, 0x000003e3, 0x0233e8fa, + 0x3fef843b, 0x000003e7, 0x00f430e4, + 0x3fbf8456, 0x3ff00bea, 0x00046cc8, + 0x3f8f8c72, 0x3ff00bef, 0x3f3490ac }, + }, + { 0x1e00, { + 0x001fbbf4, 0x001fbbf4, 0x09425112, + 0x001fa800, 0x002fc7ed, 0x0792b110, + 0x000f980e, 0x001fdbe6, 0x0613110a, + 0x3fff8c20, 0x001fe7e3, 0x04a368fd, + 0x3fcf8c33, 0x000ff7e2, 0x0343b8ed, + 0x3f9f8c4a, 0x000fffe3, 0x0203f8da, + 0x3f5f9c61, 0x000003e6, 0x00e428c5, + 0x3f1fb07b, 0x000003eb, 0x3fe440af }, + }, + { 0x2000, { + 0x000fa400, 0x000fa400, 0x09625902, + 0x3fff980c, 0x001fb7f5, 0x0812b0ff, + 0x3fdf901c, 0x001fc7ed, 0x06b2fcfa, + 0x3faf902d, 0x001fd3e8, 0x055348f1, + 0x3f7f983f, 0x001fe3e5, 0x04038ce3, + 0x3f3fa454, 0x001fefe3, 0x02e3c8d1, + 0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0, + 0x3ecfd880, 0x000fffe6, 0x00c404ac }, + }, + { 0x2200, { + 0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4, + 0x3fbf9818, 0x3fffa400, 0x0842a8f1, + 0x3f8f9827, 0x000fb3f7, 0x0702f0ec, + 0x3f5fa037, 0x000fc3ef, 0x05d330e4, + 0x3f2fac49, 0x001fcfea, 0x04a364d9, + 0x3effc05c, 0x001fdbe7, 0x038394ca, + 0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb, + 0x3ea00083, 0x001fefe6, 0x0183c0a9 }, + }, + { 0x2400, { + 0x3f9fa014, 0x3f9fa014, 0x098260e6, + 0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5, + 0x3f4fa431, 0x3fefa400, 0x0742d8e1, + 0x3f1fb440, 0x3fffb3f8, 0x062310d9, + 0x3eefc850, 0x000fbbf2, 0x050340d0, + 0x3ecfe062, 0x000fcbec, 0x041364c2, + 0x3ea00073, 0x001fd3ea, 0x03037cb5, + 0x3e902086, 0x001fdfe8, 0x022388a5 }, + }, + { 0x2600, { + 0x3f5fa81e, 0x3f5fa81e, 0x096258da, + 0x3f3fac2b, 0x3f8fa412, 0x088290d8, + 0x3f0fbc38, 0x3fafa408, 0x0772c8d5, + 0x3eefcc47, 0x3fcfa800, 0x0672f4ce, + 0x3ecfe456, 0x3fefaffa, 0x05531cc6, + 0x3eb00066, 0x3fffbbf3, 0x047334bb, + 0x3ea01c77, 0x000fc7ee, 0x039348ae, + 0x3ea04486, 0x000fd3eb, 0x02b350a1 }, + }, + { 0x2800, { + 0x3f2fb426, 0x3f2fb426, 0x094250ce, + 0x3f0fc032, 0x3f4fac1b, 0x086284cd, + 0x3eefd040, 0x3f7fa811, 0x0782acc9, + 0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4, + 0x3eb0005b, 0x3fbfac00, 0x05b2f4bc, + 0x3eb0186a, 0x3fdfb3fa, 0x04c308b4, + 0x3eb04077, 0x3fefbbf4, 0x03f31ca8, + 0x3ec06884, 0x000fbff2, 0x03031c9e }, + }, + { 0x2a00, { + 0x3f0fc42d, 0x3f0fc42d, 0x090240c4, + 0x3eefd439, 0x3f2fb822, 0x08526cc2, + 0x3edfe845, 0x3f4fb018, 0x078294bf, + 0x3ec00051, 0x3f6fac0f, 0x06b2b4bb, + 0x3ec0185f, 0x3f8fac07, 0x05e2ccb4, + 0x3ec0386b, 0x3fafac00, 0x0502e8ac, + 0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3, + 0x3ef08482, 0x3fdfbbf6, 0x0372f898 }, + }, + { 0x2c00, { + 0x3eefdc31, 0x3eefdc31, 0x08e238b8, + 0x3edfec3d, 0x3f0fc828, 0x082258b9, + 0x3ed00049, 0x3f1fc01e, 0x077278b6, + 0x3ed01455, 0x3f3fb815, 0x06c294b2, + 0x3ed03460, 0x3f5fb40d, 0x0602acac, + 0x3ef0506c, 0x3f7fb006, 0x0542c0a4, + 0x3f107476, 0x3f9fb400, 0x0472c89d, + 0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 }, + }, + { 0x2e00, { + 0x3eefec37, 0x3eefec37, 0x088220b0, + 0x3ee00041, 0x3effdc2d, 0x07f244ae, + 0x3ee0144c, 0x3f0fd023, 0x07625cad, + 0x3ef02c57, 0x3f1fc81a, 0x06c274a9, + 0x3f004861, 0x3f3fbc13, 0x060288a6, + 0x3f20686b, 0x3f5fb80c, 0x05529c9e, + 0x3f408c74, 0x3f6fb805, 0x04b2ac96, + 0x3f80ac7e, 0x3f8fb800, 0x0402ac8e }, + }, + { 0x3000, { + 0x3ef0003a, 0x3ef0003a, 0x084210a6, + 0x3ef01045, 0x3effec32, 0x07b228a7, + 0x3f00284e, 0x3f0fdc29, 0x073244a4, + 0x3f104058, 0x3f0fd420, 0x06a258a2, + 0x3f305c62, 0x3f2fc818, 0x0612689d, + 0x3f508069, 0x3f3fc011, 0x05728496, + 0x3f80a072, 0x3f4fc00a, 0x04d28c90, + 0x3fc0c07b, 0x3f6fbc04, 0x04429088 }, + }, + { 0x3200, { + 0x3f00103e, 0x3f00103e, 0x07f1fc9e, + 0x3f102447, 0x3f000035, 0x0782149d, + 0x3f203c4f, 0x3f0ff02c, 0x07122c9c, + 0x3f405458, 0x3f0fe424, 0x06924099, + 0x3f607061, 0x3f1fd41d, 0x06024c97, + 0x3f909068, 0x3f2fcc16, 0x05726490, + 0x3fc0b070, 0x3f3fc80f, 0x04f26c8a, + 0x0000d077, 0x3f4fc409, 0x04627484 }, + }, + { 0x3400, { + 0x3f202040, 0x3f202040, 0x07a1e898, + 0x3f303449, 0x3f100c38, 0x0741fc98, + 0x3f504c50, 0x3f10002f, 0x06e21495, + 0x3f706459, 0x3f1ff028, 0x06722492, + 0x3fa08060, 0x3f1fe421, 0x05f2348f, + 0x3fd09c67, 0x3f1fdc19, 0x05824c89, + 0x0000bc6e, 0x3f2fd014, 0x04f25086, + 0x0040dc74, 0x3f3fcc0d, 0x04825c7f }, + }, + { 0x3600, { + 0x3f403042, 0x3f403042, 0x0761d890, + 0x3f504848, 0x3f301c3b, 0x0701f090, + 0x3f805c50, 0x3f200c33, 0x06a2008f, + 0x3fa07458, 0x3f10002b, 0x06520c8d, + 0x3fd0905e, 0x3f1ff424, 0x05e22089, + 0x0000ac65, 0x3f1fe81d, 0x05823483, + 0x0030cc6a, 0x3f2fdc18, 0x04f23c81, + 0x0080e871, 0x3f2fd412, 0x0482407c }, + }, + { 0x3800, { + 0x3f604043, 0x3f604043, 0x0721c88a, + 0x3f80544a, 0x3f502c3c, 0x06d1d88a, + 0x3fb06851, 0x3f301c35, 0x0681e889, + 0x3fd08456, 0x3f30082f, 0x0611fc88, + 0x00009c5d, 0x3f200027, 0x05d20884, + 0x0030b863, 0x3f2ff421, 0x05621880, + 0x0070d468, 0x3f2fe81b, 0x0502247c, + 0x00c0ec6f, 0x3f2fe015, 0x04a22877 }, + }, + { 0x3a00, { + 0x3f904c44, 0x3f904c44, 0x06e1b884, + 0x3fb0604a, 0x3f70383e, 0x0691c885, + 0x3fe07451, 0x3f502c36, 0x0661d483, + 0x00009055, 0x3f401831, 0x0601ec81, + 0x0030a85b, 0x3f300c2a, 0x05b1f480, + 0x0070c061, 0x3f300024, 0x0562047a, + 0x00b0d867, 0x3f3ff41e, 0x05020c77, + 0x00f0f46b, 0x3f2fec19, 0x04a21474 }, + }, + { 0x3c00, { + 0x3fb05c43, 0x3fb05c43, 0x06c1b07e, + 0x3fe06c4b, 0x3f902c3f, 0x0681c081, + 0x0000844f, 0x3f703838, 0x0631cc7d, + 0x00309855, 0x3f602433, 0x05d1d47e, + 0x0060b459, 0x3f50142e, 0x0581e47b, + 0x00a0c85f, 0x3f400828, 0x0531f078, + 0x00e0e064, 0x3f300021, 0x0501fc73, + 0x00b0fc6a, 0x3f3ff41d, 0x04a20873 }, + }, + { 0x3e00, { + 0x3fe06444, 0x3fe06444, 0x0681a07a, + 0x00007849, 0x3fc0503f, 0x0641b07a, + 0x0020904d, 0x3fa0403a, 0x05f1c07a, + 0x0060a453, 0x3f803034, 0x05c1c878, + 0x0090b858, 0x3f70202f, 0x0571d477, + 0x00d0d05d, 0x3f501829, 0x0531e073, + 0x0110e462, 0x3f500825, 0x04e1e471, + 0x01510065, 0x3f40001f, 0x04a1f06d }, + }, + { 0x4000, { + 0x00007044, 0x00007044, 0x06519476, + 0x00208448, 0x3fe05c3f, 0x0621a476, + 0x0050984d, 0x3fc04c3a, 0x05e1b075, + 0x0080ac52, 0x3fa03c35, 0x05a1b875, + 0x00c0c056, 0x3f803030, 0x0561c473, + 0x0100d45b, 0x3f70202b, 0x0521d46f, + 0x0140e860, 0x3f601427, 0x04d1d46e, + 0x01810064, 0x3f500822, 0x0491dc6b }, + }, + { 0x5000, { + 0x0110a442, 0x0110a442, 0x0551545e, + 0x0140b045, 0x00e0983f, 0x0531585f, + 0x0160c047, 0x00c08c3c, 0x0511645e, + 0x0190cc4a, 0x00908039, 0x04f1685f, + 0x01c0dc4c, 0x00707436, 0x04d1705e, + 0x0200e850, 0x00506833, 0x04b1785b, + 0x0230f453, 0x00305c30, 0x0491805a, + 0x02710056, 0x0010542d, 0x04718059 }, + }, + { 0x6000, { + 0x01c0bc40, 0x01c0bc40, 0x04c13052, + 0x01e0c841, 0x01a0b43d, 0x04c13851, + 0x0210cc44, 0x0180a83c, 0x04a13453, + 0x0230d845, 0x0160a03a, 0x04913c52, + 0x0260e047, 0x01409838, 0x04714052, + 0x0280ec49, 0x01208c37, 0x04514c50, + 0x02b0f44b, 0x01008435, 0x04414c50, + 0x02d1004c, 0x00e07c33, 0x0431544f }, + }, + { 0x7000, { + 0x0230c83e, 0x0230c83e, 0x04711c4c, + 0x0250d03f, 0x0210c43c, 0x0471204b, + 0x0270d840, 0x0200b83c, 0x0451244b, + 0x0290dc42, 0x01e0b43a, 0x0441244c, + 0x02b0e443, 0x01c0b038, 0x0441284b, + 0x02d0ec44, 0x01b0a438, 0x0421304a, + 0x02f0f445, 0x0190a036, 0x04213449, + 0x0310f847, 0x01709c34, 0x04213848 }, + }, + { 0x8000, { + 0x0280d03d, 0x0280d03d, 0x04310c48, + 0x02a0d43e, 0x0270c83c, 0x04311047, + 0x02b0dc3e, 0x0250c83a, 0x04311447, + 0x02d0e040, 0x0240c03a, 0x04211446, + 0x02e0e840, 0x0220bc39, 0x04111847, + 0x0300e842, 0x0210b438, 0x04012445, + 0x0310f043, 0x0200b037, 0x04012045, + 0x0330f444, 0x01e0ac36, 0x03f12445 }, + }, + { 0xefff, { + 0x0340dc3a, 0x0340dc3a, 0x03b0ec40, + 0x0340e03a, 0x0330e039, 0x03c0f03e, + 0x0350e03b, 0x0330dc39, 0x03c0ec3e, + 0x0350e43a, 0x0320dc38, 0x03c0f43e, + 0x0360e43b, 0x0320d839, 0x03b0f03e, + 0x0360e83b, 0x0310d838, 0x03c0fc3b, + 0x0370e83b, 0x0310d439, 0x03a0f83d, + 0x0370e83c, 0x0300d438, 0x03b0fc3c }, + } +}; + +static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs) +{ + int i; + const struct vin_coeff *p_prev_set = NULL; + const struct vin_coeff *p_set = NULL; + + /* Look for suitable coefficient values */ + for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) { + p_prev_set = p_set; + p_set = &vin_coeff_set[i]; + + if (xs < p_set->xs_value) + break; + } + + /* Use previous value if its XS value is closer */ + if (p_prev_set && p_set && + xs - p_prev_set->xs_value < p_set->xs_value - xs) + p_set = p_prev_set; + + /* Set coefficient registers */ + rvin_write(vin, p_set->coeff_set[0], VNC1A_REG); + rvin_write(vin, p_set->coeff_set[1], VNC1B_REG); + rvin_write(vin, p_set->coeff_set[2], VNC1C_REG); + + rvin_write(vin, p_set->coeff_set[3], VNC2A_REG); + rvin_write(vin, p_set->coeff_set[4], VNC2B_REG); + rvin_write(vin, p_set->coeff_set[5], VNC2C_REG); + + rvin_write(vin, p_set->coeff_set[6], VNC3A_REG); + rvin_write(vin, p_set->coeff_set[7], VNC3B_REG); + rvin_write(vin, p_set->coeff_set[8], VNC3C_REG); + + rvin_write(vin, p_set->coeff_set[9], VNC4A_REG); + rvin_write(vin, p_set->coeff_set[10], VNC4B_REG); + rvin_write(vin, p_set->coeff_set[11], VNC4C_REG); + + rvin_write(vin, p_set->coeff_set[12], VNC5A_REG); + rvin_write(vin, p_set->coeff_set[13], VNC5B_REG); + rvin_write(vin, p_set->coeff_set[14], VNC5C_REG); + + rvin_write(vin, p_set->coeff_set[15], VNC6A_REG); + rvin_write(vin, p_set->coeff_set[16], VNC6B_REG); + rvin_write(vin, p_set->coeff_set[17], VNC6C_REG); + + rvin_write(vin, p_set->coeff_set[18], VNC7A_REG); + rvin_write(vin, p_set->coeff_set[19], VNC7B_REG); + rvin_write(vin, p_set->coeff_set[20], VNC7C_REG); + + rvin_write(vin, p_set->coeff_set[21], VNC8A_REG); + rvin_write(vin, p_set->coeff_set[22], VNC8B_REG); + rvin_write(vin, p_set->coeff_set[23], VNC8C_REG); +} + +void rvin_crop_scale_comp(struct rvin_dev *vin) +{ + u32 xs, ys; + + /* Set Start/End Pixel/Line Pre-Clip */ + rvin_write(vin, vin->crop.left, VNSPPRC_REG); + rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG); + switch (vin->format.field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG); + rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1, + VNELPRC_REG); + break; + default: + rvin_write(vin, vin->crop.top, VNSLPRC_REG); + rvin_write(vin, vin->crop.top + vin->crop.height - 1, + VNELPRC_REG); + break; + } + + /* Set scaling coefficient */ + ys = 0; + if (vin->crop.height != vin->compose.height) + ys = (4096 * vin->crop.height) / vin->compose.height; + rvin_write(vin, ys, VNYS_REG); + + xs = 0; + if (vin->crop.width != vin->compose.width) + xs = (4096 * vin->crop.width) / vin->compose.width; + + /* Horizontal upscaling is up to double size */ + if (xs > 0 && xs < 2048) + xs = 2048; + + rvin_write(vin, xs, VNXS_REG); + + /* Horizontal upscaling is done out by scaling down from double size */ + if (xs < 4096) + xs *= 2; + + rvin_set_coeff(vin, xs); + + /* Set Start/End Pixel/Line Post-Clip */ + rvin_write(vin, 0, VNSPPOC_REG); + rvin_write(vin, 0, VNSLPOC_REG); + rvin_write(vin, vin->format.width - 1, VNEPPOC_REG); + switch (vin->format.field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG); + break; + default: + rvin_write(vin, vin->format.height - 1, VNELPOC_REG); + break; + } + + if (vin->format.pixelformat == V4L2_PIX_FMT_NV16) + rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG); + else + rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG); + + vin_dbg(vin, + "Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n", + vin->crop.width, vin->crop.height, vin->crop.left, + vin->crop.top, ys, xs, vin->format.width, vin->format.height, + 0, 0); +} + +void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix, + u32 width, u32 height) +{ + /* All VIN channels on Gen2 have scalers */ + pix->width = width; + pix->height = height; +} + +/* ----------------------------------------------------------------------------- + * DMA Functions + */ + +#define RVIN_TIMEOUT_MS 100 +#define RVIN_RETRIES 10 + +struct rvin_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ + struct rvin_buffer, \ + vb)->list) + +/* Moves a buffer from the queue to the HW slots */ +static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot) +{ + struct rvin_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + dma_addr_t phys_addr_top; + + if (vin->queue_buf[slot] != NULL) + return true; + + if (list_empty(&vin->buf_list)) + return false; + + vin_dbg(vin, "Filling HW slot: %d\n", slot); + + /* Keep track of buffer we give to HW */ + buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); + vbuf = &buf->vb; + list_del_init(to_buf_list(vbuf)); + vin->queue_buf[slot] = vbuf; + + /* Setup DMA */ + phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + rvin_set_slot_addr(vin, slot, phys_addr_top); + + return true; +} + +static bool rvin_fill_hw(struct rvin_dev *vin) +{ + int slot, limit; + + limit = vin->continuous ? HW_BUFFER_NUM : 1; + + for (slot = 0; slot < limit; slot++) + if (!rvin_fill_hw_slot(vin, slot)) + return false; + return true; +} + +static irqreturn_t rvin_irq(int irq, void *data) +{ + struct rvin_dev *vin = data; + u32 int_status; + int slot; + unsigned int sequence, handled = 0; + unsigned long flags; + + spin_lock_irqsave(&vin->qlock, flags); + + int_status = rvin_get_interrupt_status(vin); + if (!int_status) + goto done; + + rvin_ack_interrupt(vin); + handled = 1; + + /* Nothing to do if capture status is 'STOPPED' */ + if (vin->state == STOPPED) { + vin_dbg(vin, "IRQ while state stopped\n"); + goto done; + } + + /* Nothing to do if capture status is 'STOPPING' */ + if (vin->state == STOPPING) { + vin_dbg(vin, "IRQ while state stopping\n"); + goto done; + } + + /* Prepare for capture and update state */ + slot = rvin_get_active_slot(vin); + sequence = vin->sequence++; + + vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n", + sequence, slot, + slot == 0 ? 'x' : vin->queue_buf[0] != NULL ? '1' : '0', + slot == 1 ? 'x' : vin->queue_buf[1] != NULL ? '1' : '0', + slot == 2 ? 'x' : vin->queue_buf[2] != NULL ? '1' : '0', + !list_empty(&vin->buf_list)); + + /* HW have written to a slot that is not prepared we are in trouble */ + if (WARN_ON((vin->queue_buf[slot] == NULL))) + goto done; + + /* Capture frame */ + vin->queue_buf[slot]->field = vin->format.field; + vin->queue_buf[slot]->sequence = sequence; + vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE); + vin->queue_buf[slot] = NULL; + + /* Prepare for next frame */ + if (!rvin_fill_hw(vin)) { + + /* + * Can't supply HW with new buffers fast enough. Halt + * capture until more buffers are available. + */ + vin->state = STALLED; + + /* + * The continuous capturing requires an explicit stop + * operation when there is no buffer to be set into + * the VnMBm registers. + */ + if (vin->continuous) { + rvin_capture_off(vin); + vin_dbg(vin, "IRQ %02d: hw not ready stop\n", sequence); + } + } else { + /* + * The single capturing requires an explicit capture + * operation to fetch the next frame. + */ + if (!vin->continuous) + rvin_capture_on(vin); + } +done: + spin_unlock_irqrestore(&vin->qlock, flags); + + return IRQ_RETVAL(handled); +} + +/* Need to hold qlock before calling */ +static void return_all_buffers(struct rvin_dev *vin, + enum vb2_buffer_state state) +{ + struct rvin_buffer *buf, *node; + int i; + + for (i = 0; i < HW_BUFFER_NUM; i++) { + if (vin->queue_buf[i]) { + vb2_buffer_done(&vin->queue_buf[i]->vb2_buf, + state); + vin->queue_buf[i] = NULL; + } + } + + list_for_each_entry_safe(buf, node, &vin->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } +} + +static int rvin_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + void *alloc_ctxs[]) + +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + + alloc_ctxs[0] = vin->alloc_ctx; + /* Make sure the image size is large enough. */ + if (*nplanes) + return sizes[0] < vin->format.sizeimage ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = vin->format.sizeimage; + + return 0; +}; + +static int rvin_buffer_prepare(struct vb2_buffer *vb) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = vin->format.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + vin_err(vin, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void rvin_buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue); + unsigned long flags; + + spin_lock_irqsave(&vin->qlock, flags); + + list_add_tail(to_buf_list(vbuf), &vin->buf_list); + + /* + * If capture is stalled add buffer to HW and restart + * capturing if HW is ready to continue. + */ + if (vin->state == STALLED) + if (rvin_fill_hw(vin)) + rvin_capture_on(vin); + + spin_unlock_irqrestore(&vin->qlock, flags); +} + +static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + struct v4l2_subdev *sd; + unsigned long flags; + int ret; + + sd = vin_to_source(vin); + v4l2_subdev_call(sd, video, s_stream, 1); + + spin_lock_irqsave(&vin->qlock, flags); + + vin->state = RUNNING; + vin->sequence = 0; + + /* Continuous capture requires more buffers then there are HW slots */ + vin->continuous = count > HW_BUFFER_NUM; + + /* + * This should never happen but if we don't have enough + * buffers for HW bail out + */ + if (!rvin_fill_hw(vin)) { + vin_err(vin, "HW not ready to start, not enough buffers available\n"); + ret = -EINVAL; + goto out; + } + + ret = rvin_capture_start(vin); +out: + /* Return all buffers if something went wrong */ + if (ret) { + return_all_buffers(vin, VB2_BUF_STATE_QUEUED); + v4l2_subdev_call(sd, video, s_stream, 0); + } + + spin_unlock_irqrestore(&vin->qlock, flags); + + return ret; +} + +static void rvin_stop_streaming(struct vb2_queue *vq) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + struct v4l2_subdev *sd; + unsigned long flags; + int retries = 0; + + spin_lock_irqsave(&vin->qlock, flags); + + vin->state = STOPPING; + + /* Wait for streaming to stop */ + while (retries++ < RVIN_RETRIES) { + + rvin_capture_stop(vin); + + /* Check if HW is stopped */ + if (!rvin_capture_active(vin)) { + vin->state = STOPPED; + break; + } + + spin_unlock_irqrestore(&vin->qlock, flags); + msleep(RVIN_TIMEOUT_MS); + spin_lock_irqsave(&vin->qlock, flags); + } + + if (vin->state != STOPPED) { + /* + * If this happens something have gone horribly wrong. + * Set state to stopped to prevent the interrupt handler + * to make things worse... + */ + vin_err(vin, "Failed stop HW, something is seriously broken\n"); + vin->state = STOPPED; + } + + /* Release all active buffers */ + return_all_buffers(vin, VB2_BUF_STATE_ERROR); + + spin_unlock_irqrestore(&vin->qlock, flags); + + sd = vin_to_source(vin); + v4l2_subdev_call(sd, video, s_stream, 0); + + /* disable interrupts */ + rvin_disable_interrupts(vin); +} + +static struct vb2_ops rvin_qops = { + .queue_setup = rvin_queue_setup, + .buf_prepare = rvin_buffer_prepare, + .buf_queue = rvin_buffer_queue, + .start_streaming = rvin_start_streaming, + .stop_streaming = rvin_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +void rvin_dma_remove(struct rvin_dev *vin) +{ + if (!IS_ERR_OR_NULL(vin->alloc_ctx)) + vb2_dma_contig_cleanup_ctx(vin->alloc_ctx); + + mutex_destroy(&vin->lock); + + v4l2_device_unregister(&vin->v4l2_dev); +} + +int rvin_dma_probe(struct rvin_dev *vin, int irq) +{ + struct vb2_queue *q = &vin->queue; + int i, ret; + + /* Initialize the top-level structure */ + ret = v4l2_device_register(vin->dev, &vin->v4l2_dev); + if (ret) + return ret; + + mutex_init(&vin->lock); + INIT_LIST_HEAD(&vin->buf_list); + + spin_lock_init(&vin->qlock); + + vin->state = STOPPED; + + for (i = 0; i < HW_BUFFER_NUM; i++) + vin->queue_buf[i] = NULL; + + /* buffer queue */ + vin->alloc_ctx = vb2_dma_contig_init_ctx(vin->dev); + if (IS_ERR(vin->alloc_ctx)) { + ret = PTR_ERR(vin->alloc_ctx); + goto error; + } + + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; + q->lock = &vin->lock; + q->drv_priv = vin; + q->buf_struct_size = sizeof(struct rvin_buffer); + q->ops = &rvin_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + + ret = vb2_queue_init(q); + if (ret < 0) { + vin_err(vin, "failed to initialize VB2 queue\n"); + goto error; + } + + /* irq */ + ret = devm_request_irq(vin->dev, irq, rvin_irq, IRQF_SHARED, + KBUILD_MODNAME, vin); + if (ret) { + vin_err(vin, "failed to request irq\n"); + goto error; + } + + return 0; +error: + rvin_dma_remove(vin); + + return ret; +} diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c new file mode 100644 index 000000000000..0bc4487e2482 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -0,0 +1,768 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * 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. + */ + +#include <linux/pm_runtime.h> + +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-rect.h> + +#include "rcar-vin.h" + +#define RVIN_DEFAULT_FORMAT V4L2_PIX_FMT_YUYV +#define RVIN_MAX_WIDTH 2048 +#define RVIN_MAX_HEIGHT 2048 + +/* ----------------------------------------------------------------------------- + * Format Conversions + */ + +static const struct rvin_video_format rvin_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .bpp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_XRGB555, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .bpp = 4, + }, +}; + +const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) + if (rvin_formats[i].fourcc == pixelformat) + return rvin_formats + i; + + return NULL; +} + +static u32 rvin_format_bytesperline(struct v4l2_pix_format *pix) +{ + const struct rvin_video_format *fmt; + + fmt = rvin_format_from_pixel(pix->pixelformat); + + if (WARN_ON(!fmt)) + return -EINVAL; + + return pix->width * fmt->bpp; +} + +static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix) +{ + if (pix->pixelformat == V4L2_PIX_FMT_NV16) + return pix->bytesperline * pix->height * 2; + + return pix->bytesperline * pix->height; +} + +/* ----------------------------------------------------------------------------- + * V4L2 + */ + +static int __rvin_try_format_source(struct rvin_dev *vin, + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) +{ + struct v4l2_subdev *sd; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = which, + }; + int ret; + + sd = vin_to_source(vin); + + v4l2_fill_mbus_format(&format.format, pix, vin->source.code); + + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, pad, set_fmt, + &pad_cfg, &format); + if (ret < 0) + return ret; + + v4l2_fill_pix_format(pix, &format.format); + + source->width = pix->width; + source->height = pix->height; + + vin_dbg(vin, "Source resolution: %ux%u\n", source->width, + source->height); + + return 0; +} + +static int __rvin_try_format(struct rvin_dev *vin, + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) +{ + const struct rvin_video_format *info; + u32 rwidth, rheight, walign; + + /* Requested */ + rwidth = pix->width; + rheight = pix->height; + + /* + * Retrieve format information and select the current format if the + * requested format isn't supported. + */ + info = rvin_format_from_pixel(pix->pixelformat); + if (!info) { + vin_dbg(vin, "Format %x not found, keeping %x\n", + pix->pixelformat, vin->format.pixelformat); + *pix = vin->format; + pix->width = rwidth; + pix->height = rheight; + } + + /* Always recalculate */ + pix->bytesperline = 0; + pix->sizeimage = 0; + + /* Limit to source capabilities */ + __rvin_try_format_source(vin, which, pix, source); + + /* If source can't match format try if VIN can scale */ + if (source->width != rwidth || source->height != rheight) + rvin_scale_try(vin, pix, rwidth, rheight); + + /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ + walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; + + /* Limit to VIN capabilities */ + v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign, + &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0); + + switch (pix->field) { + case V4L2_FIELD_NONE: + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: + break; + default: + pix->field = V4L2_FIELD_NONE; + break; + } + + pix->bytesperline = max_t(u32, pix->bytesperline, + rvin_format_bytesperline(pix)); + pix->sizeimage = max_t(u32, pix->sizeimage, + rvin_format_sizeimage(pix)); + + vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n", + rwidth, rheight, pix->width, pix->height, + pix->bytesperline, pix->sizeimage); + + return 0; +} + +static int rvin_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rvin_dev *vin = video_drvdata(file); + + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(vin->dev)); + return 0; +} + +static int rvin_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + struct rvin_source_fmt source; + + return __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, + &source); +} + +static int rvin_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + struct rvin_source_fmt source; + int ret; + + if (vb2_is_busy(&vin->queue)) + return -EBUSY; + + ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, + &source); + if (ret) + return ret; + + vin->source.width = source.width; + vin->source.height = source.height; + + vin->format = f->fmt.pix; + + return 0; +} + +static int rvin_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + + f->fmt.pix = vin->format; + + return 0; +} + +static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(rvin_formats)) + return -EINVAL; + + f->pixelformat = rvin_formats[f->index].fourcc; + + return 0; +} + +static int rvin_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct rvin_dev *vin = video_drvdata(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = s->r.top = 0; + s->r.width = vin->source.width; + s->r.height = vin->source.height; + break; + case V4L2_SEL_TGT_CROP: + s->r = vin->crop; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.left = s->r.top = 0; + s->r.width = vin->format.width; + s->r.height = vin->format.height; + break; + case V4L2_SEL_TGT_COMPOSE: + s->r = vin->compose; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rvin_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct rvin_dev *vin = video_drvdata(file); + const struct rvin_video_format *fmt; + struct v4l2_rect r = s->r; + struct v4l2_rect max_rect; + struct v4l2_rect min_rect = { + .width = 6, + .height = 2, + }; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + v4l2_rect_set_min_size(&r, &min_rect); + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + /* Can't crop outside of source input */ + max_rect.top = max_rect.left = 0; + max_rect.width = vin->source.width; + max_rect.height = vin->source.height; + v4l2_rect_map_inside(&r, &max_rect); + + v4l_bound_align_image(&r.width, 2, vin->source.width, 1, + &r.height, 4, vin->source.height, 2, 0); + + r.top = clamp_t(s32, r.top, 0, vin->source.height - r.height); + r.left = clamp_t(s32, r.left, 0, vin->source.width - r.width); + + vin->crop = s->r = r; + + vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n", + r.width, r.height, r.left, r.top, + vin->source.width, vin->source.height); + break; + case V4L2_SEL_TGT_COMPOSE: + /* Make sure compose rect fits inside output format */ + max_rect.top = max_rect.left = 0; + max_rect.width = vin->format.width; + max_rect.height = vin->format.height; + v4l2_rect_map_inside(&r, &max_rect); + + /* + * Composing is done by adding a offset to the buffer address, + * the HW wants this address to be aligned to HW_BUFFER_MASK. + * Make sure the top and left values meets this requirement. + */ + while ((r.top * vin->format.bytesperline) & HW_BUFFER_MASK) + r.top--; + + fmt = rvin_format_from_pixel(vin->format.pixelformat); + while ((r.left * fmt->bpp) & HW_BUFFER_MASK) + r.left--; + + vin->compose = s->r = r; + + vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n", + r.width, r.height, r.left, r.top, + vin->format.width, vin->format.height); + break; + default: + return -EINVAL; + } + + /* HW supports modifying configuration while running */ + rvin_crop_scale_comp(vin); + + return 0; +} + +static int rvin_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_subdev_call(sd, video, cropcap, crop); +} + +static int rvin_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int ret; + + if (i->index != 0) + return -EINVAL; + + ret = v4l2_subdev_call(sd, video, g_input_status, &i->status); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + i->type = V4L2_INPUT_TYPE_CAMERA; + i->std = vin->vdev.tvnorms; + strlcpy(i->name, "Camera", sizeof(i->name)); + + return 0; +} + +static int rvin_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int rvin_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + return 0; +} + +static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + return v4l2_subdev_call(sd, video, querystd, a); +} + +static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret = v4l2_subdev_call(sd, video, s_std, a); + + if (ret < 0) + return ret; + + /* Changing the standard will change the width/height */ + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret) { + vin_err(vin, "Failed to get initial format\n"); + return ret; + } + + vin->format.width = mf->width; + vin->format.height = mf->height; + + vin->crop.top = vin->crop.left = 0; + vin->crop.width = mf->width; + vin->crop.height = mf->height; + + vin->compose.top = vin->compose.left = 0; + vin->compose.width = mf->width; + vin->compose.height = mf->height; + + return 0; +} + +static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + return v4l2_subdev_call(sd, video, g_std, a); +} + +static int rvin_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_event_subscribe(fh, sub, 4, NULL); + } + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static const struct v4l2_ioctl_ops rvin_ioctl_ops = { + .vidioc_querycap = rvin_querycap, + .vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = rvin_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = rvin_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap, + + .vidioc_g_selection = rvin_g_selection, + .vidioc_s_selection = rvin_s_selection, + + .vidioc_cropcap = rvin_cropcap, + + .vidioc_enum_input = rvin_enum_input, + .vidioc_g_input = rvin_g_input, + .vidioc_s_input = rvin_s_input, + + .vidioc_querystd = rvin_querystd, + .vidioc_g_std = rvin_g_std, + .vidioc_s_std = rvin_s_std, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = rvin_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ----------------------------------------------------------------------------- + * File Operations + */ + +static int rvin_power_on(struct rvin_dev *vin) +{ + int ret; + struct v4l2_subdev *sd = vin_to_source(vin); + + pm_runtime_get_sync(vin->v4l2_dev.dev); + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + return 0; +} + +static int rvin_power_off(struct rvin_dev *vin) +{ + int ret; + struct v4l2_subdev *sd = vin_to_source(vin); + + ret = v4l2_subdev_call(sd, core, s_power, 0); + + pm_runtime_put(vin->v4l2_dev.dev); + + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + return 0; +} + +static int rvin_initialize_device(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + int ret; + + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = vin->format.width, + .height = vin->format.height, + .field = vin->format.field, + .colorspace = vin->format.colorspace, + .pixelformat = vin->format.pixelformat, + }, + }; + + ret = rvin_power_on(vin); + if (ret < 0) + return ret; + + pm_runtime_enable(&vin->vdev.dev); + ret = pm_runtime_resume(&vin->vdev.dev); + if (ret < 0 && ret != -ENOSYS) + goto eresume; + + /* + * Try to configure with default parameters. Notice: this is the + * very first open, so, we cannot race against other calls, + * apart from someone else calling open() simultaneously, but + * .host_lock is protecting us against it. + */ + ret = rvin_s_fmt_vid_cap(file, NULL, &f); + if (ret < 0) + goto esfmt; + + v4l2_ctrl_handler_setup(&vin->ctrl_handler); + + return 0; +esfmt: + pm_runtime_disable(&vin->vdev.dev); +eresume: + rvin_power_off(vin); + + return ret; +} + +static int rvin_open(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + int ret; + + mutex_lock(&vin->lock); + + file->private_data = vin; + + ret = v4l2_fh_open(file); + if (ret) + goto unlock; + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + if (rvin_initialize_device(file)) { + v4l2_fh_release(file); + ret = -ENODEV; + } + +unlock: + mutex_unlock(&vin->lock); + return ret; +} + +static int rvin_release(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + bool fh_singular; + int ret; + + mutex_lock(&vin->lock); + + /* Save the singular status before we call the clean-up helper */ + fh_singular = v4l2_fh_is_singular_file(file); + + /* the release helper will cleanup any on-going streaming */ + ret = _vb2_fop_release(file, NULL); + + /* + * If this was the last open file. + * Then de-initialize hw module. + */ + if (fh_singular) { + pm_runtime_suspend(&vin->vdev.dev); + pm_runtime_disable(&vin->vdev.dev); + rvin_power_off(vin); + } + + mutex_unlock(&vin->lock); + + return ret; +} + +static const struct v4l2_file_operations rvin_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = rvin_open, + .release = rvin_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +void rvin_v4l2_remove(struct rvin_dev *vin) +{ + v4l2_info(&vin->v4l2_dev, "Removing %s\n", + video_device_node_name(&vin->vdev)); + + /* Checks internaly if handlers have been init or not */ + v4l2_ctrl_handler_free(&vin->ctrl_handler); + + /* Checks internaly if vdev have been init or not */ + video_unregister_device(&vin->vdev); +} + +static void rvin_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct rvin_dev *vin = + container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev); + + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: + v4l2_event_queue(&vin->vdev, arg); + break; + default: + break; + } +} + +int rvin_v4l2_probe(struct rvin_dev *vin) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + struct video_device *vdev = &vin->vdev; + struct v4l2_subdev *sd = vin_to_source(vin); + int ret; + + v4l2_set_subdev_hostdata(sd, vin); + + vin->v4l2_dev.notify = rvin_notify; + + ret = v4l2_subdev_call(sd, video, g_tvnorms, &vin->vdev.tvnorms); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (vin->vdev.tvnorms == 0) { + /* Disable the STD API if there are no tvnorms defined */ + v4l2_disable_ioctl(&vin->vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(&vin->vdev, VIDIOC_S_STD); + v4l2_disable_ioctl(&vin->vdev, VIDIOC_QUERYSTD); + v4l2_disable_ioctl(&vin->vdev, VIDIOC_ENUMSTD); + } + + /* Add the controls */ + /* + * Currently the subdev with the largest number of controls (13) is + * ov6550. So let's pick 16 as a hint for the control handler. Note + * that this is a hint only: too large and you waste some memory, too + * small and there is a (very) small performance hit when looking up + * controls in the internal hash. + */ + ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); + if (ret < 0) + return ret; + + ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, sd->ctrl_handler, NULL); + if (ret < 0) + return ret; + + /* video node */ + vdev->fops = &rvin_fops; + vdev->v4l2_dev = &vin->v4l2_dev; + vdev->queue = &vin->queue; + strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->ioctl_ops = &rvin_ioctl_ops; + vdev->lock = &vin->lock; + vdev->ctrl_handler = &vin->ctrl_handler; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + + /* Try to improve our guess of a reasonable window format */ + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret) { + vin_err(vin, "Failed to get initial format\n"); + return ret; + } + + /* Set default format */ + vin->format.width = mf->width; + vin->format.height = mf->height; + vin->format.colorspace = mf->colorspace; + vin->format.field = mf->field; + vin->format.pixelformat = RVIN_DEFAULT_FORMAT; + + + /* Set initial crop and compose */ + vin->crop.top = vin->crop.left = 0; + vin->crop.width = mf->width; + vin->crop.height = mf->height; + + vin->compose.top = vin->compose.left = 0; + vin->compose.width = mf->width; + vin->compose.height = mf->height; + + ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + vin_err(vin, "Failed to register video device\n"); + return ret; + } + + video_set_drvdata(&vin->vdev, vin); + + v4l2_info(&vin->v4l2_dev, "Device registered as %s\n", + video_device_node_name(&vin->vdev)); + + return ret; +} diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h new file mode 100644 index 000000000000..544a3b362808 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -0,0 +1,163 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * 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. + */ + +#ifndef __RCAR_VIN__ +#define __RCAR_VIN__ + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-v4l2.h> + +/* Number of HW buffers */ +#define HW_BUFFER_NUM 3 + +/* Address alignment mask for HW buffers */ +#define HW_BUFFER_MASK 0x7f + +enum chip_id { + RCAR_GEN2, + RCAR_H1, + RCAR_M1, +}; + +/** + * STOPPED - No operation in progress + * RUNNING - Operation in progress have buffers + * STALLED - No operation in progress have no buffers + * STOPPING - Stopping operation + */ +enum rvin_dma_state { + STOPPED = 0, + RUNNING, + STALLED, + STOPPING, +}; + +/** + * struct rvin_source_fmt - Source information + * @code: Media bus format from source + * @width: Width from source + * @height: Height from source + */ +struct rvin_source_fmt { + u32 code; + u32 width; + u32 height; +}; + +/** + * struct rvin_video_format - Data format stored in memory + * @fourcc: Pixelformat + * @bpp: Bytes per pixel + */ +struct rvin_video_format { + u32 fourcc; + u8 bpp; +}; + +struct rvin_graph_entity { + struct device_node *node; + struct media_entity *entity; + + struct v4l2_async_subdev asd; + struct v4l2_subdev *subdev; +}; + +/** + * struct rvin_dev - Renesas VIN device structure + * @dev: (OF) device + * @base: device I/O register space remapped to virtual memory + * @chip: type of VIN chip + * @mbus_cfg media bus configuration + * + * @vdev: V4L2 video device associated with VIN + * @v4l2_dev: V4L2 device + * @ctrl_handler: V4L2 control handler + * @notifier: V4L2 asynchronous subdevs notifier + * @entity: entity in the DT for subdevice + * + * @lock: protects @queue + * @queue: vb2 buffers queue + * @alloc_ctx: allocation context for the vb2 @queue + * + * @qlock: protects @queue_buf, @buf_list, @continuous, @sequence + * @state + * @queue_buf: Keeps track of buffers given to HW slot + * @buf_list: list of queued buffers + * @continuous: tracks if active operation is continuous or single mode + * @sequence: V4L2 buffers sequence number + * @state: keeps track of operation state + * + * @source: active format from the video source + * @format: active V4L2 pixel format + * + * @crop: active cropping + * @compose: active composing + */ +struct rvin_dev { + struct device *dev; + void __iomem *base; + enum chip_id chip; + struct v4l2_mbus_config mbus_cfg; + + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_async_notifier notifier; + struct rvin_graph_entity entity; + + struct mutex lock; + struct vb2_queue queue; + struct vb2_alloc_ctx *alloc_ctx; + + spinlock_t qlock; + struct vb2_v4l2_buffer *queue_buf[HW_BUFFER_NUM]; + struct list_head buf_list; + bool continuous; + unsigned int sequence; + enum rvin_dma_state state; + + struct rvin_source_fmt source; + struct v4l2_pix_format format; + + struct v4l2_rect crop; + struct v4l2_rect compose; +}; + +#define vin_to_source(vin) vin->entity.subdev + +/* Debug */ +#define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg) +#define vin_info(d, fmt, arg...) dev_info(d->dev, fmt, ##arg) +#define vin_warn(d, fmt, arg...) dev_warn(d->dev, fmt, ##arg) +#define vin_err(d, fmt, arg...) dev_err(d->dev, fmt, ##arg) + +int rvin_dma_probe(struct rvin_dev *vin, int irq); +void rvin_dma_remove(struct rvin_dev *vin); + +int rvin_v4l2_probe(struct rvin_dev *vin); +void rvin_v4l2_remove(struct rvin_dev *vin); + +const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat); + +/* Cropping, composing and scaling */ +void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix, + u32 width, u32 height); +void rvin_crop_scale_comp(struct rvin_dev *vin); + +#endif diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 612d1ea514f1..d3e3469db8de 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -681,6 +681,7 @@ static int g2d_probe(struct platform_device *pdev) goto put_clk_gate; } + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(dev->alloc_ctx)) { ret = PTR_ERR(dev->alloc_ctx); @@ -757,6 +758,7 @@ static int g2d_remove(struct platform_device *pdev) video_unregister_device(dev->vfd); v4l2_device_unregister(&dev->v4l2_dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); clk_unprepare(dev->gate); clk_put(dev->gate); clk_unprepare(dev->clk); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index caa19b408551..17bc94092864 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2843,6 +2843,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) goto device_register_rollback; } + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); jpeg->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(jpeg->alloc_ctx)) { v4l2_err(&jpeg->v4l2_dev, "Failed to init memory allocator\n"); @@ -2942,6 +2943,7 @@ static int s5p_jpeg_remove(struct platform_device *pdev) video_unregister_device(jpeg->vfd_decoder); video_unregister_device(jpeg->vfd_encoder); vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index b16466fe35ee..6ee620ee8cd5 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -22,6 +22,7 @@ #include <media/v4l2-event.h> #include <linux/workqueue.h> #include <linux/of.h> +#include <linux/of_reserved_mem.h> #include <media/videobuf2-v4l2.h> #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" @@ -29,6 +30,7 @@ #include "s5p_mfc_dec.h" #include "s5p_mfc_enc.h" #include "s5p_mfc_intr.h" +#include "s5p_mfc_iommu.h" #include "s5p_mfc_opr.h" #include "s5p_mfc_cmd.h" #include "s5p_mfc_pm.h" @@ -1043,55 +1045,94 @@ static const struct v4l2_file_operations s5p_mfc_fops = { .mmap = s5p_mfc_mmap, }; -static int match_child(struct device *dev, void *data) +/* DMA memory related helper functions */ +static void s5p_mfc_memdev_release(struct device *dev) { - if (!dev_name(dev)) - return 0; - return !strcmp(dev_name(dev), (char *)data); + of_reserved_mem_device_release(dev); } -static void *mfc_get_drv_data(struct platform_device *pdev); - -static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) +static struct device *s5p_mfc_alloc_memdev(struct device *dev, + const char *name, unsigned int idx) { - unsigned int mem_info[2] = { }; + struct device *child; + int ret; - dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev, - sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_l) { - mfc_err("Not enough memory\n"); - return -ENOMEM; - } - device_initialize(dev->mem_dev_l); - of_property_read_u32_array(dev->plat_dev->dev.of_node, - "samsung,mfc-l", mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - mfc_err("Failed to declare coherent memory for\n" - "MFC device\n"); - return -ENOMEM; + child = devm_kzalloc(dev, sizeof(struct device), GFP_KERNEL); + if (!child) + return NULL; + + device_initialize(child); + dev_set_name(child, "%s:%s", dev_name(dev), name); + child->parent = dev; + child->bus = dev->bus; + child->coherent_dma_mask = dev->coherent_dma_mask; + child->dma_mask = dev->dma_mask; + child->release = s5p_mfc_memdev_release; + + if (device_add(child) == 0) { + ret = of_reserved_mem_device_init_by_idx(child, dev->of_node, + idx); + if (ret == 0) + return child; } - dev->mem_dev_r = devm_kzalloc(&dev->plat_dev->dev, - sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_r) { - mfc_err("Not enough memory\n"); - return -ENOMEM; + put_device(child); + return NULL; +} + +static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev) +{ + struct device *dev = &mfc_dev->plat_dev->dev; + + /* + * When IOMMU is available, we cannot use the default configuration, + * because of MFC firmware requirements: address space limited to + * 256M and non-zero default start address. + * This is still simplified, not optimal configuration, but for now + * IOMMU core doesn't allow to configure device's IOMMUs channel + * separately. + */ + if (exynos_is_iommu_available(dev)) { + int ret = exynos_configure_iommu(dev, S5P_MFC_IOMMU_DMA_BASE, + S5P_MFC_IOMMU_DMA_SIZE); + if (ret == 0) + mfc_dev->mem_dev_l = mfc_dev->mem_dev_r = dev; + return ret; } - device_initialize(dev->mem_dev_r); - of_property_read_u32_array(dev->plat_dev->dev.of_node, - "samsung,mfc-r", mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - pr_err("Failed to declare coherent memory for\n" - "MFC device\n"); - return -ENOMEM; + + /* + * Create and initialize virtual devices for accessing + * reserved memory regions. + */ + mfc_dev->mem_dev_l = s5p_mfc_alloc_memdev(dev, "left", + MFC_BANK1_ALLOC_CTX); + if (!mfc_dev->mem_dev_l) + return -ENODEV; + mfc_dev->mem_dev_r = s5p_mfc_alloc_memdev(dev, "right", + MFC_BANK2_ALLOC_CTX); + if (!mfc_dev->mem_dev_r) { + device_unregister(mfc_dev->mem_dev_l); + return -ENODEV; } + return 0; } +static void s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev *mfc_dev) +{ + struct device *dev = &mfc_dev->plat_dev->dev; + + if (exynos_is_iommu_available(dev)) { + exynos_unconfigure_iommu(dev); + return; + } + + device_unregister(mfc_dev->mem_dev_l); + device_unregister(mfc_dev->mem_dev_r); +} + +static void *mfc_get_drv_data(struct platform_device *pdev); + /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1117,12 +1158,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) dev->variant = mfc_get_drv_data(pdev); - ret = s5p_mfc_init_pm(dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get mfc clock source\n"); - return ret; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->regs_base = devm_ioremap_resource(&pdev->dev, res); @@ -1143,32 +1178,25 @@ static int s5p_mfc_probe(struct platform_device *pdev) goto err_res; } - if (pdev->dev.of_node) { - ret = s5p_mfc_alloc_memdevs(dev); - if (ret < 0) - goto err_res; - } else { - dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, - "s5p-mfc-l", match_child); - if (!dev->mem_dev_l) { - mfc_err("Mem child (L) device get failed\n"); - ret = -ENODEV; - goto err_res; - } - dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, - "s5p-mfc-r", match_child); - if (!dev->mem_dev_r) { - mfc_err("Mem child (R) device get failed\n"); - ret = -ENODEV; - goto err_res; - } + ret = s5p_mfc_configure_dma_memory(dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to configure DMA memory\n"); + return ret; } + ret = s5p_mfc_init_pm(dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get mfc clock source\n"); + return ret; + } + + vb2_dma_contig_set_max_seg_size(dev->mem_dev_l, DMA_BIT_MASK(32)); dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); if (IS_ERR(dev->alloc_ctx[0])) { ret = PTR_ERR(dev->alloc_ctx[0]); goto err_res; } + vb2_dma_contig_set_max_seg_size(dev->mem_dev_r, DMA_BIT_MASK(32)); dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r); if (IS_ERR(dev->alloc_ctx[1])) { ret = PTR_ERR(dev->alloc_ctx[1]); @@ -1201,14 +1229,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_dec_reg; - } - v4l2_info(&dev->v4l2_dev, - "decoder registered as /dev/video%d\n", vfd->num); video_set_drvdata(vfd, dev); /* encoder */ @@ -1226,14 +1246,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); dev->vfd_enc = vfd; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_enc_reg; - } - v4l2_info(&dev->v4l2_dev, - "encoder registered as /dev/video%d\n", vfd->num); video_set_drvdata(vfd, dev); platform_set_drvdata(pdev, dev); @@ -1250,15 +1262,34 @@ static int s5p_mfc_probe(struct platform_device *pdev) s5p_mfc_init_hw_cmds(dev); s5p_mfc_init_regs(dev); + /* Register decoder and encoder */ + ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + video_device_release(dev->vfd_dec); + goto err_dec_reg; + } + v4l2_info(&dev->v4l2_dev, + "decoder registered as /dev/video%d\n", dev->vfd_dec->num); + + ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + video_device_release(dev->vfd_enc); + goto err_enc_reg; + } + v4l2_info(&dev->v4l2_dev, + "encoder registered as /dev/video%d\n", dev->vfd_enc->num); + pr_debug("%s--\n", __func__); return 0; /* Deinit MFC if probe had failed */ err_enc_reg: - video_device_release(dev->vfd_enc); -err_enc_alloc: video_unregister_device(dev->vfd_dec); err_dec_reg: + video_device_release(dev->vfd_enc); +err_enc_alloc: video_device_release(dev->vfd_dec); err_dec_alloc: v4l2_device_unregister(&dev->v4l2_dev); @@ -1293,10 +1324,9 @@ static int s5p_mfc_remove(struct platform_device *pdev) s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); - if (pdev->dev.of_node) { - put_device(dev->mem_dev_l); - put_device(dev->mem_dev_r); - } + s5p_mfc_unconfigure_dma_memory(dev); + vb2_dma_contig_clear_max_seg_size(dev->mem_dev_l); + vb2_dma_contig_clear_max_seg_size(dev->mem_dev_r); s5p_mfc_final_pm(dev); return 0; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index f2d6376ce618..a01a373a4c4f 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -474,7 +474,6 @@ static int reqbufs_output(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx, ret = vb2_reqbufs(&ctx->vq_src, reqbufs); if (ret) goto out; - s5p_mfc_close_mfc_inst(dev, ctx); ctx->src_bufs_cnt = 0; ctx->output_state = QUEUE_FREE; } else if (ctx->output_state == QUEUE_FREE) { @@ -573,7 +572,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); if (reqbufs->memory != V4L2_MEMORY_MMAP) { - mfc_err("Only V4L2_MEMORY_MAP is supported\n"); + mfc_err("Only V4L2_MEMORY_MMAP is supported\n"); return -EINVAL; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 034b5c1d35a1..2f76aba4cc78 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1043,10 +1043,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("failed to try output format\n"); return -EINVAL; } - if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { - mfc_err("must be set encoding output size\n"); - return -EINVAL; - } if ((dev->variant->version_bit & fmt->versions) == 0) { mfc_err("Unsupported format by this MFC version.\n"); return -EINVAL; @@ -1060,11 +1056,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("failed to try output format\n"); return -EINVAL; } - - if (fmt->num_planes != pix_fmt_mp->num_planes) { - mfc_err("failed to try output format\n"); - return -EINVAL; - } if ((dev->variant->version_bit & fmt->versions) == 0) { mfc_err("Unsupported format by this MFC version.\n"); return -EINVAL; @@ -1144,7 +1135,10 @@ static int vidioc_reqbufs(struct file *file, void *priv, return -EINVAL; if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (reqbufs->count == 0) { + mfc_debug(2, "Freeing buffers\n"); ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); + s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, + ctx); ctx->capture_state = QUEUE_FREE; return ret; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h new file mode 100644 index 000000000000..6962132ae8fa --- /dev/null +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co.Ltd + * Authors: Marek Szyprowski <m.szyprowski@samsung.com> + * + * 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. + */ + +#ifndef S5P_MFC_IOMMU_H_ +#define S5P_MFC_IOMMU_H_ + +#define S5P_MFC_IOMMU_DMA_BASE 0x20000000lu +#define S5P_MFC_IOMMU_DMA_SIZE SZ_256M + +#if defined(CONFIG_EXYNOS_IOMMU) && defined(CONFIG_ARM_DMA_USE_IOMMU) + +#include <asm/dma-iommu.h> + +static inline bool exynos_is_iommu_available(struct device *dev) +{ + return dev->archdata.iommu != NULL; +} + +static inline void exynos_unconfigure_iommu(struct device *dev) +{ + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(mapping); +} + +static inline int exynos_configure_iommu(struct device *dev, + unsigned int base, unsigned int size) +{ + struct dma_iommu_mapping *mapping = NULL; + int ret; + + /* Disable the default mapping created by device core */ + if (to_dma_iommu_mapping(dev)) + exynos_unconfigure_iommu(dev); + + mapping = arm_iommu_create_mapping(dev->bus, base, size); + if (IS_ERR(mapping)) { + pr_warn("Failed to create IOMMU mapping for device %s\n", + dev_name(dev)); + return PTR_ERR(mapping); + } + + ret = arm_iommu_attach_device(dev, mapping); + if (ret) { + pr_warn("Failed to attached device %s to IOMMU_mapping\n", + dev_name(dev)); + arm_iommu_release_mapping(mapping); + return ret; + } + + return 0; +} + +#else + +static inline bool exynos_is_iommu_available(struct device *dev) +{ + return false; +} + +static inline int exynos_configure_iommu(struct device *dev, + unsigned int base, unsigned int size) +{ + return -ENOSYS; +} + +static inline void exynos_unconfigure_iommu(struct device *dev) { } + +#endif + +#endif /* S5P_MFC_IOMMU_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 5f97a3398c11..9f7522104333 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -54,6 +54,7 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) pm->clock = clk_get(&dev->plat_dev->dev, MFC_SCLK_NAME); if (IS_ERR(pm->clock)) { mfc_info("Failed to get MFC special clock control\n"); + pm->clock = NULL; } else { clk_set_rate(pm->clock, MFC_SCLK_RATE); ret = clk_prepare_enable(pm->clock); diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 7ab5578a0405..123d27107f60 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -80,6 +80,7 @@ int mxr_acquire_video(struct mxr_device *mdev, goto fail; } + vb2_dma_contig_set_max_seg_size(mdev->dev, DMA_BIT_MASK(32)); mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev); if (IS_ERR(mdev->alloc_ctx)) { mxr_err(mdev, "could not acquire vb2 allocator\n"); @@ -152,6 +153,7 @@ void mxr_release_video(struct mxr_device *mdev) kfree(mdev->output[i]); vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(mdev->dev); v4l2_device_unregister(&mdev->v4l2_dev); } diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 83029a4854ae..39f66414f621 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -25,8 +25,8 @@ config VIDEO_PXA27x ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface -config VIDEO_RCAR_VIN - tristate "R-Car Video Input (VIN) support" +config VIDEO_RCAR_VIN_OLD + tristate "R-Car Video Input (VIN) support (DEPRECATED)" depends on VIDEO_DEV && SOC_CAMERA depends on ARCH_RENESAS || COMPILE_TEST depends on HAS_DMA diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 7ee71ae231c7..7703cb7ce456 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -10,4 +10,4 @@ obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o -obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar_vin.o +obj-$(CONFIG_VIDEO_RCAR_VIN_OLD) += rcar_vin.o diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 95b3ac2ea7ef..1328e1bd2143 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -1,7 +1,8 @@ vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_pipe.o vsp1-y += vsp1_dl.o vsp1_drm.o vsp1_video.o vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o -vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o +vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o +vsp1-y += vsp1_lif.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 46738b6c5f72..06a2ec7e5ad4 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -25,11 +25,13 @@ struct clk; struct device; +struct rcar_fcp_device; struct vsp1_drm; struct vsp1_entity; struct vsp1_platform_data; struct vsp1_bru; +struct vsp1_clu; struct vsp1_hsit; struct vsp1_lif; struct vsp1_lut; @@ -45,6 +47,9 @@ struct vsp1_uds; #define VSP1_HAS_LUT (1 << 1) #define VSP1_HAS_SRU (1 << 2) #define VSP1_HAS_BRU (1 << 3) +#define VSP1_HAS_CLU (1 << 4) +#define VSP1_HAS_WPF_VFLIP (1 << 5) +#define VSP1_HAS_WPF_HFLIP (1 << 6) struct vsp1_device_info { u32 version; @@ -62,12 +67,10 @@ struct vsp1_device { const struct vsp1_device_info *info; void __iomem *mmio; - struct clk *clock; - - struct mutex lock; - int ref_count; + struct rcar_fcp_device *fcp; struct vsp1_bru *bru; + struct vsp1_clu *clu; struct vsp1_hsit *hsi; struct vsp1_hsit *hst; struct vsp1_lif *lif; diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index b1068c018011..8268b87727a7 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -249,7 +249,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_subdev_pad_ops bru_pad_ops = { +static const struct v4l2_subdev_pad_ops bru_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = bru_enum_mbus_code, .enum_frame_size = bru_enum_frame_size, @@ -259,7 +259,7 @@ static struct v4l2_subdev_pad_ops bru_pad_ops = { .set_selection = bru_set_selection, }; -static struct v4l2_subdev_ops bru_ops = { +static const struct v4l2_subdev_ops bru_ops = { .pad = &bru_pad_ops, }; @@ -269,13 +269,16 @@ static struct v4l2_subdev_ops bru_ops = { static void bru_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_bru *bru = to_bru(&entity->subdev); struct v4l2_mbus_framefmt *format; unsigned int flags; unsigned int i; + if (!full) + return; + format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config, bru->entity.source_pad); @@ -390,7 +393,8 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) bru->entity.type = VSP1_ENTITY_BRU; ret = vsp1_entity_init(vsp1, &bru->entity, "bru", - vsp1->info->num_bru_inputs + 1, &bru_ops); + vsp1->info->num_bru_inputs + 1, &bru_ops, + MEDIA_ENT_F_PROC_VIDEO_COMPOSER); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c new file mode 100644 index 000000000000..b63d2dbe5ea3 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_clu.c @@ -0,0 +1,292 @@ +/* + * vsp1_clu.c -- R-Car VSP1 Cubic Look-Up Table + * + * Copyright (C) 2015-2016 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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. + */ + +#include <linux/device.h> +#include <linux/slab.h> + +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_clu.h" +#include "vsp1_dl.h" + +#define CLU_MIN_SIZE 4U +#define CLU_MAX_SIZE 8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline void vsp1_clu_write(struct vsp1_clu *clu, struct vsp1_dl_list *dl, + u32 reg, u32 data) +{ + vsp1_dl_list_write(dl, reg, data); +} + +/* ----------------------------------------------------------------------------- + * Controls + */ + +#define V4L2_CID_VSP1_CLU_TABLE (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_VSP1_CLU_MODE (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_VSP1_CLU_MODE_2D 0 +#define V4L2_CID_VSP1_CLU_MODE_3D 1 + +static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl) +{ + struct vsp1_dl_body *dlb; + unsigned int i; + + dlb = vsp1_dl_fragment_alloc(clu->entity.vsp1, 1 + 17 * 17 * 17); + if (!dlb) + return -ENOMEM; + + vsp1_dl_fragment_write(dlb, VI6_CLU_ADDR, 0); + for (i = 0; i < 17 * 17 * 17; ++i) + vsp1_dl_fragment_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); + + spin_lock_irq(&clu->lock); + swap(clu->clu, dlb); + spin_unlock_irq(&clu->lock); + + vsp1_dl_fragment_free(dlb); + return 0; +} + +static int clu_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_clu *clu = + container_of(ctrl->handler, struct vsp1_clu, ctrls); + + switch (ctrl->id) { + case V4L2_CID_VSP1_CLU_TABLE: + clu_set_table(clu, ctrl); + break; + + case V4L2_CID_VSP1_CLU_MODE: + clu->mode = ctrl->val; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops clu_ctrl_ops = { + .s_ctrl = clu_s_ctrl, +}; + +static const struct v4l2_ctrl_config clu_table_control = { + .ops = &clu_ctrl_ops, + .id = V4L2_CID_VSP1_CLU_TABLE, + .name = "Look-Up Table", + .type = V4L2_CTRL_TYPE_U32, + .min = 0x00000000, + .max = 0x00ffffff, + .step = 1, + .def = 0, + .dims = { 17, 17, 17 }, +}; + +static const char * const clu_mode_menu[] = { + "2D", + "3D", + NULL, +}; + +static const struct v4l2_ctrl_config clu_mode_control = { + .ops = &clu_ctrl_ops, + .id = V4L2_CID_VSP1_CLU_MODE, + .name = "Mode", + .type = V4L2_CTRL_TYPE_MENU, + .min = 0, + .max = 1, + .def = 1, + .qmenu = clu_mode_menu, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int clu_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AHSV8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, + }; + + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + ARRAY_SIZE(codes)); +} + +static int clu_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + return vsp1_subdev_enum_frame_size(subdev, cfg, fse, CLU_MIN_SIZE, + CLU_MIN_SIZE, CLU_MAX_SIZE, + CLU_MAX_SIZE); +} + +static int clu_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_clu *clu = to_clu(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + + config = vsp1_entity_get_pad_config(&clu->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + /* Default to YUV if the requested format is not supported. */ + if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && + fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 && + fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) + fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; + + format = vsp1_entity_get_pad_format(&clu->entity, config, fmt->pad); + + if (fmt->pad == CLU_PAD_SOURCE) { + /* The CLU output format can't be modified. */ + fmt->format = *format; + return 0; + } + + format->code = fmt->format.code; + format->width = clamp_t(unsigned int, fmt->format.width, + CLU_MIN_SIZE, CLU_MAX_SIZE); + format->height = clamp_t(unsigned int, fmt->format.height, + CLU_MIN_SIZE, CLU_MAX_SIZE); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&clu->entity, config, + CLU_PAD_SOURCE); + *format = fmt->format; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static const struct v4l2_subdev_pad_ops clu_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, + .enum_mbus_code = clu_enum_mbus_code, + .enum_frame_size = clu_enum_frame_size, + .get_fmt = vsp1_subdev_get_pad_format, + .set_fmt = clu_set_format, +}; + +static const struct v4l2_subdev_ops clu_ops = { + .pad = &clu_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void clu_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, bool full) +{ + struct vsp1_clu *clu = to_clu(&entity->subdev); + struct vsp1_dl_body *dlb; + unsigned long flags; + u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; + + /* The format can't be changed during streaming, only verify it at + * stream start and store the information internally for future partial + * reconfiguration calls. + */ + if (full) { + struct v4l2_mbus_framefmt *format; + + format = vsp1_entity_get_pad_format(&clu->entity, + clu->entity.config, + CLU_PAD_SINK); + clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; + return; + } + + /* 2D mode can only be used with the YCbCr pixel encoding. */ + if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) + ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D + | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D + | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; + + vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); + + spin_lock_irqsave(&clu->lock, flags); + dlb = clu->clu; + clu->clu = NULL; + spin_unlock_irqrestore(&clu->lock, flags); + + if (dlb) + vsp1_dl_list_add_fragment(dl, dlb); +} + +static const struct vsp1_entity_operations clu_entity_ops = { + .configure = clu_configure, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1) +{ + struct vsp1_clu *clu; + int ret; + + clu = devm_kzalloc(vsp1->dev, sizeof(*clu), GFP_KERNEL); + if (clu == NULL) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&clu->lock); + + clu->entity.ops = &clu_entity_ops; + clu->entity.type = VSP1_ENTITY_CLU; + + ret = vsp1_entity_init(vsp1, &clu->entity, "clu", 2, &clu_ops, + MEDIA_ENT_F_PROC_VIDEO_LUT); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&clu->ctrls, 2); + v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL); + v4l2_ctrl_new_custom(&clu->ctrls, &clu_mode_control, NULL); + + clu->entity.subdev.ctrl_handler = &clu->ctrls; + + if (clu->ctrls.error) { + dev_err(vsp1->dev, "clu: failed to initialize controls\n"); + ret = clu->ctrls.error; + vsp1_entity_destroy(&clu->entity); + return ERR_PTR(ret); + } + + v4l2_ctrl_handler_setup(&clu->ctrls); + + return clu; +} diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/vsp1/vsp1_clu.h new file mode 100644 index 000000000000..036e0a2f1a42 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_clu.h @@ -0,0 +1,48 @@ +/* + * vsp1_clu.h -- R-Car VSP1 Cubic Look-Up Table + * + * Copyright (C) 2015 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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. + */ +#ifndef __VSP1_CLU_H__ +#define __VSP1_CLU_H__ + +#include <linux/spinlock.h> + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; +struct vsp1_dl_body; + +#define CLU_PAD_SINK 0 +#define CLU_PAD_SOURCE 1 + +struct vsp1_clu { + struct vsp1_entity entity; + + struct v4l2_ctrl_handler ctrls; + + bool yuv_mode; + spinlock_t lock; + unsigned int mode; + struct vsp1_dl_body *clu; +}; + +static inline struct vsp1_clu *to_clu(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_clu, entity.subdev); +} + +struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_CLU_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index e238d9b9376b..37c3518aa2a8 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -15,6 +15,7 @@ #include <linux/dma-mapping.h> #include <linux/gfp.h> #include <linux/slab.h> +#include <linux/workqueue.h> #include "vsp1.h" #include "vsp1_dl.h" @@ -92,11 +93,13 @@ enum vsp1_dl_mode { * @index: index of the related WPF * @mode: display list operation mode (header or headerless) * @vsp1: the VSP1 device - * @lock: protects the active, queued and pending lists + * @lock: protects the free, active, queued, pending and gc_fragments lists * @free: array of all free display lists * @active: list currently being processed (loaded) by hardware * @queued: list queued to the hardware (written to the DL registers) * @pending: list waiting to be queued to the hardware + * @gc_work: fragments garbage collector work struct + * @gc_fragments: array of display list fragments waiting to be freed */ struct vsp1_dl_manager { unsigned int index; @@ -108,6 +111,9 @@ struct vsp1_dl_manager { struct vsp1_dl_list *active; struct vsp1_dl_list *queued; struct vsp1_dl_list *pending; + + struct work_struct gc_work; + struct list_head gc_fragments; }; /* ----------------------------------------------------------------------------- @@ -262,21 +268,10 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) return dl; } -static void vsp1_dl_list_free_fragments(struct vsp1_dl_list *dl) -{ - struct vsp1_dl_body *dlb, *next; - - list_for_each_entry_safe(dlb, next, &dl->fragments, list) { - list_del(&dlb->list); - vsp1_dl_body_cleanup(dlb); - kfree(dlb); - } -} - static void vsp1_dl_list_free(struct vsp1_dl_list *dl) { vsp1_dl_body_cleanup(&dl->body0); - vsp1_dl_list_free_fragments(dl); + list_splice_init(&dl->fragments, &dl->dlm->gc_fragments); kfree(dl); } @@ -311,7 +306,16 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) if (!dl) return; - vsp1_dl_list_free_fragments(dl); + /* We can't free fragments here as DMA memory can only be freed in + * interruptible context. Move all fragments to the display list + * manager's list of fragments to be freed, they will be + * garbage-collected by the work queue. + */ + if (!list_empty(&dl->fragments)) { + list_splice_init(&dl->fragments, &dl->dlm->gc_fragments); + schedule_work(&dl->dlm->gc_work); + } + dl->body0.num_entries = 0; list_add_tail(&dl->list, &dl->dlm->free); @@ -550,6 +554,40 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) dlm->pending = NULL; } +/* + * Free all fragments awaiting to be garbage-collected. + * + * This function must be called without the display list manager lock held. + */ +static void vsp1_dlm_fragments_free(struct vsp1_dl_manager *dlm) +{ + unsigned long flags; + + spin_lock_irqsave(&dlm->lock, flags); + + while (!list_empty(&dlm->gc_fragments)) { + struct vsp1_dl_body *dlb; + + dlb = list_first_entry(&dlm->gc_fragments, struct vsp1_dl_body, + list); + list_del(&dlb->list); + + spin_unlock_irqrestore(&dlm->lock, flags); + vsp1_dl_fragment_free(dlb); + spin_lock_irqsave(&dlm->lock, flags); + } + + spin_unlock_irqrestore(&dlm->lock, flags); +} + +static void vsp1_dlm_garbage_collect(struct work_struct *work) +{ + struct vsp1_dl_manager *dlm = + container_of(work, struct vsp1_dl_manager, gc_work); + + vsp1_dlm_fragments_free(dlm); +} + struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, unsigned int index, unsigned int prealloc) @@ -568,6 +606,8 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, spin_lock_init(&dlm->lock); INIT_LIST_HEAD(&dlm->free); + INIT_LIST_HEAD(&dlm->gc_fragments); + INIT_WORK(&dlm->gc_work, vsp1_dlm_garbage_collect); for (i = 0; i < prealloc; ++i) { struct vsp1_dl_list *dl; @@ -589,8 +629,12 @@ void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm) if (!dlm) return; + cancel_work_sync(&dlm->gc_work); + list_for_each_entry_safe(dl, next, &dlm->free, list) { list_del(&dl->list); vsp1_dl_list_free(dl); } + + vsp1_dlm_fragments_free(dlm); } diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index fc4bbc401e67..fe9665e57b3b 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -230,42 +230,33 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); * vsp1_du_atomic_update - Setup one RPF input of the VSP pipeline * @dev: the VSP device * @rpf_index: index of the RPF to setup (0-based) - * @pixelformat: V4L2 pixel format for the RPF memory input - * @pitch: number of bytes per line in the image stored in memory - * @mem: DMA addresses of the memory buffers (one per plane) - * @src: the source crop rectangle for the RPF - * @dst: the destination compose rectangle for the BRU input - * @alpha: global alpha value for the input - * @zpos: the Z-order position of the input + * @cfg: the RPF configuration * - * Configure the VSP to perform composition of the image referenced by @mem - * through RPF @rpf_index, using the @src crop rectangle and the @dst + * Configure the VSP to perform image composition through RPF @rpf_index as + * described by the @cfg configuration. The image to compose is referenced by + * @cfg.mem and composed using the @cfg.src crop rectangle and the @cfg.dst * composition rectangle. The Z-order is configurable with higher @zpos values * displayed on top. * - * Image format as stored in memory is expressed as a V4L2 @pixelformat value. - * As a special case, setting the pixel format to 0 will disable the RPF. The - * @pitch, @mem, @src and @dst parameters are ignored in that case. Calling the + * If the @cfg configuration is NULL, the RPF will be disabled. Calling the * function on a disabled RPF is allowed. * - * The memory pitch is configurable to allow for padding at end of lines, or - * simple for images that extend beyond the crop rectangle boundaries. The - * @pitch value is expressed in bytes and applies to all planes for multiplanar - * formats. + * Image format as stored in memory is expressed as a V4L2 @cfg.pixelformat + * value. The memory pitch is configurable to allow for padding at end of lines, + * or simply for images that extend beyond the crop rectangle boundaries. The + * @cfg.pitch value is expressed in bytes and applies to all planes for + * multiplanar formats. * * The source memory buffer is referenced by the DMA address of its planes in - * the @mem array. Up to two planes are supported. The second plane DMA address - * is ignored for formats using a single plane. + * the @cfg.mem array. Up to two planes are supported. The second plane DMA + * address is ignored for formats using a single plane. * * This function isn't reentrant, the caller needs to serialize calls. * * Return 0 on success or a negative error code on failure. */ -int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index, - u32 pixelformat, unsigned int pitch, - dma_addr_t mem[2], const struct v4l2_rect *src, - const struct v4l2_rect *dst, unsigned int alpha, - unsigned int zpos) +int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, + const struct vsp1_du_atomic_config *cfg) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); const struct vsp1_format_info *fmtinfo; @@ -276,7 +267,7 @@ int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index, rpf = vsp1->rpf[rpf_index]; - if (pixelformat == 0) { + if (!cfg) { dev_dbg(vsp1->dev, "%s: RPF%u: disable requested\n", __func__, rpf_index); @@ -287,38 +278,39 @@ int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index, dev_dbg(vsp1->dev, "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad } zpos %u\n", __func__, rpf_index, - src->left, src->top, src->width, src->height, - dst->left, dst->top, dst->width, dst->height, - pixelformat, pitch, &mem[0], &mem[1], zpos); + cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height, + cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height, + cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1], + cfg->zpos); /* Store the format, stride, memory buffer address, crop and compose * rectangles and Z-order position and for the input. */ - fmtinfo = vsp1_get_format_info(pixelformat); + fmtinfo = vsp1_get_format_info(cfg->pixelformat); if (!fmtinfo) { dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n", - pixelformat); + cfg->pixelformat); return -EINVAL; } rpf->fmtinfo = fmtinfo; rpf->format.num_planes = fmtinfo->planes; - rpf->format.plane_fmt[0].bytesperline = pitch; - rpf->format.plane_fmt[1].bytesperline = pitch; - rpf->alpha = alpha; + rpf->format.plane_fmt[0].bytesperline = cfg->pitch; + rpf->format.plane_fmt[1].bytesperline = cfg->pitch; + rpf->alpha = cfg->alpha; - rpf->mem.addr[0] = mem[0]; - rpf->mem.addr[1] = mem[1]; + rpf->mem.addr[0] = cfg->mem[0]; + rpf->mem.addr[1] = cfg->mem[1]; rpf->mem.addr[2] = 0; - vsp1->drm->inputs[rpf_index].crop = *src; - vsp1->drm->inputs[rpf_index].compose = *dst; - vsp1->drm->inputs[rpf_index].zpos = zpos; + vsp1->drm->inputs[rpf_index].crop = cfg->src; + vsp1->drm->inputs[rpf_index].compose = cfg->dst; + vsp1->drm->inputs[rpf_index].zpos = cfg->zpos; vsp1->drm->inputs[rpf_index].enabled = true; return 0; } -EXPORT_SYMBOL_GPL(vsp1_du_atomic_update_ext); +EXPORT_SYMBOL_GPL(vsp1_du_atomic_update); static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf, unsigned int bru_input) @@ -499,8 +491,10 @@ void vsp1_du_atomic_flush(struct device *dev) vsp1_entity_route_setup(entity, pipe->dl); - if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl); + if (entity->ops->configure) { + entity->ops->configure(entity, pipe, pipe->dl, true); + entity->ops->configure(entity, pipe, pipe->dl, false); + } /* The memory buffer address must be applied after configuring * the RPF to make sure the crop offset are computed. diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index e2d779fac0eb..e1377ffe3d68 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -19,12 +19,15 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/videodev2.h> +#include <media/rcar-fcp.h> #include <media/v4l2-subdev.h> #include "vsp1.h" #include "vsp1_bru.h" +#include "vsp1_clu.h" #include "vsp1_dl.h" #include "vsp1_drm.h" #include "vsp1_hsit.h" @@ -145,7 +148,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1) return ret; } - if (vsp1->info->features & VSP1_HAS_LIF) { + if (vsp1->lif) { ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE, &vsp1->lif->entity.subdev.entity, @@ -168,19 +171,15 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1) for (i = 0; i < vsp1->info->wpf_count; ++i) { /* Connect the video device to the WPF. All connections are - * immutable except for the WPF0 source link if a LIF is - * present. + * immutable. */ struct vsp1_rwpf *wpf = vsp1->wpf[i]; - unsigned int flags = MEDIA_LNK_FL_ENABLED; - - if (!(vsp1->info->features & VSP1_HAS_LIF) || i != 0) - flags |= MEDIA_LNK_FL_IMMUTABLE; ret = media_create_pad_link(&wpf->entity.subdev.entity, RWPF_PAD_SOURCE, &wpf->video->video.entity, 0, - flags); + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); if (ret < 0) return ret; } @@ -204,7 +203,8 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1) } v4l2_device_unregister(&vsp1->v4l2_dev); - media_device_unregister(&vsp1->media_dev); + if (vsp1->info->uapi) + media_device_unregister(&vsp1->media_dev); media_device_cleanup(&vsp1->media_dev); if (!vsp1->info->uapi) @@ -252,6 +252,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities); } + if (vsp1->info->features & VSP1_HAS_CLU) { + vsp1->clu = vsp1_clu_create(vsp1); + if (IS_ERR(vsp1->clu)) { + ret = PTR_ERR(vsp1->clu); + goto done; + } + + list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities); + } + vsp1->hsi = vsp1_hsit_create(vsp1, true); if (IS_ERR(vsp1->hsi)) { ret = PTR_ERR(vsp1->hsi); @@ -268,7 +278,11 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); - if (vsp1->info->features & VSP1_HAS_LIF) { + /* The LIF is only supported when used in conjunction with the DU, in + * which case the userspace API is disabled. If the userspace API is + * enabled skip the LIF, even when present. + */ + if (vsp1->info->features & VSP1_HAS_LIF && !vsp1->info->uapi) { vsp1->lif = vsp1_lif_create(vsp1); if (IS_ERR(vsp1->lif)) { ret = PTR_ERR(vsp1->lif); @@ -379,14 +393,15 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) /* Register subdev nodes if the userspace API is enabled or initialize * the DRM pipeline otherwise. */ - if (vsp1->info->uapi) + if (vsp1->info->uapi) { ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); - else - ret = vsp1_drm_init(vsp1); - if (ret < 0) - goto done; + if (ret < 0) + goto done; - ret = media_device_register(mdev); + ret = media_device_register(mdev); + } else { + ret = vsp1_drm_init(vsp1); + } done: if (ret < 0) @@ -462,35 +477,16 @@ static int vsp1_device_init(struct vsp1_device *vsp1) /* * vsp1_device_get - Acquire the VSP1 device * - * Increment the VSP1 reference count and initialize the device if the first - * reference is taken. + * Make sure the device is not suspended and initialize it if needed. * * Return 0 on success or a negative error code otherwise. */ int vsp1_device_get(struct vsp1_device *vsp1) { - int ret = 0; - - mutex_lock(&vsp1->lock); - if (vsp1->ref_count > 0) - goto done; - - ret = clk_prepare_enable(vsp1->clock); - if (ret < 0) - goto done; - - ret = vsp1_device_init(vsp1); - if (ret < 0) { - clk_disable_unprepare(vsp1->clock); - goto done; - } - -done: - if (!ret) - vsp1->ref_count++; + int ret; - mutex_unlock(&vsp1->lock); - return ret; + ret = pm_runtime_get_sync(vsp1->dev); + return ret < 0 ? ret : 0; } /* @@ -501,12 +497,7 @@ done: */ void vsp1_device_put(struct vsp1_device *vsp1) { - mutex_lock(&vsp1->lock); - - if (--vsp1->ref_count == 0) - clk_disable_unprepare(vsp1->clock); - - mutex_unlock(&vsp1->lock); + pm_runtime_put_sync(vsp1->dev); } /* ----------------------------------------------------------------------------- @@ -518,14 +509,8 @@ static int vsp1_pm_suspend(struct device *dev) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); - WARN_ON(mutex_is_locked(&vsp1->lock)); - - if (vsp1->ref_count == 0) - return 0; - vsp1_pipelines_suspend(vsp1); - - clk_disable_unprepare(vsp1->clock); + pm_runtime_force_suspend(vsp1->dev); return 0; } @@ -534,21 +519,39 @@ static int vsp1_pm_resume(struct device *dev) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); - WARN_ON(mutex_is_locked(&vsp1->lock)); + pm_runtime_force_resume(vsp1->dev); + vsp1_pipelines_resume(vsp1); - if (vsp1->ref_count == 0) - return 0; + return 0; +} +#endif - clk_prepare_enable(vsp1->clock); +static int vsp1_pm_runtime_suspend(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); - vsp1_pipelines_resume(vsp1); + rcar_fcp_disable(vsp1->fcp); return 0; } -#endif + +static int vsp1_pm_runtime_resume(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + int ret; + + if (vsp1->info) { + ret = vsp1_device_init(vsp1); + if (ret < 0) + return ret; + } + + return rcar_fcp_enable(vsp1->fcp); +} static const struct dev_pm_ops vsp1_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume) + SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL) }; /* ----------------------------------------------------------------------------- @@ -559,7 +562,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { { .version = VI6_IP_VERSION_MODEL_VSPS_H2, .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 3, .wpf_count = 4, @@ -568,9 +572,9 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPR_H2, .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_SRU, + .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, - .uds_count = 1, + .uds_count = 3, .wpf_count = 4, .num_bru_inputs = 4, .uapi = true, @@ -580,22 +584,24 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT, .rpf_count = 4, .uds_count = 1, - .wpf_count = 4, + .wpf_count = 1, .num_bru_inputs = 4, .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPS_M2, .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, - .uds_count = 3, + .uds_count = 1, .wpf_count = 4, .num_bru_inputs = 4, .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPI_GEN3, .gen = 3, - .features = VSP1_HAS_LUT | VSP1_HAS_SRU, + .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU + | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP, .rpf_count = 1, .uds_count = 1, .wpf_count = 1, @@ -603,7 +609,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3, .gen = 3, - .features = VSP1_HAS_BRU, + .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .wpf_count = 1, .num_bru_inputs = 5, @@ -611,7 +617,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3, .gen = 3, - .features = VSP1_HAS_BRU | VSP1_HAS_LUT, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .wpf_count = 1, .num_bru_inputs = 5, @@ -619,7 +626,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, .gen = 3, - .features = VSP1_HAS_BRU | VSP1_HAS_LIF, + .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .wpf_count = 2, .num_bru_inputs = 5, @@ -629,6 +636,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { static int vsp1_probe(struct platform_device *pdev) { struct vsp1_device *vsp1; + struct device_node *fcp_node; struct resource *irq; struct resource *io; unsigned int i; @@ -640,22 +648,17 @@ static int vsp1_probe(struct platform_device *pdev) return -ENOMEM; vsp1->dev = &pdev->dev; - mutex_init(&vsp1->lock); INIT_LIST_HEAD(&vsp1->entities); INIT_LIST_HEAD(&vsp1->videos); - /* I/O, IRQ and clock resources */ + platform_set_drvdata(pdev, vsp1); + + /* I/O and IRQ resources (clock managed by the clock PM domain) */ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); if (IS_ERR(vsp1->mmio)) return PTR_ERR(vsp1->mmio); - vsp1->clock = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(vsp1->clock)) { - dev_err(&pdev->dev, "failed to get clock\n"); - return PTR_ERR(vsp1->clock); - } - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq) { dev_err(&pdev->dev, "missing IRQ\n"); @@ -669,13 +672,27 @@ static int vsp1_probe(struct platform_device *pdev) return ret; } + /* FCP (optional) */ + fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0); + if (fcp_node) { + vsp1->fcp = rcar_fcp_get(fcp_node); + of_node_put(fcp_node); + if (IS_ERR(vsp1->fcp)) { + dev_dbg(&pdev->dev, "FCP not found (%ld)\n", + PTR_ERR(vsp1->fcp)); + return PTR_ERR(vsp1->fcp); + } + } + /* Configure device parameters based on the version register. */ - ret = clk_prepare_enable(vsp1->clock); + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) - return ret; + goto done; version = vsp1_read(vsp1, VI6_IP_VERSION); - clk_disable_unprepare(vsp1->clock); + pm_runtime_put_sync(&pdev->dev); for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { if ((version & VI6_IP_VERSION_MODEL_MASK) == @@ -687,7 +704,8 @@ static int vsp1_probe(struct platform_device *pdev) if (!vsp1->info) { dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version); - return -ENXIO; + ret = -ENXIO; + goto done; } dev_dbg(&pdev->dev, "IP version 0x%08x\n", version); @@ -696,12 +714,14 @@ static int vsp1_probe(struct platform_device *pdev) ret = vsp1_create_entities(vsp1); if (ret < 0) { dev_err(&pdev->dev, "failed to create entities\n"); - return ret; + goto done; } - platform_set_drvdata(pdev, vsp1); +done: + if (ret) + pm_runtime_disable(&pdev->dev); - return 0; + return ret; } static int vsp1_remove(struct platform_device *pdev) @@ -709,6 +729,9 @@ static int vsp1_remove(struct platform_device *pdev) struct vsp1_device *vsp1 = platform_get_drvdata(pdev); vsp1_destroy_entities(vsp1); + rcar_fcp_put(vsp1->fcp); + + pm_runtime_disable(&pdev->dev); return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 3d070bcc6053..4cf6cc719c00 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -22,6 +22,12 @@ #include "vsp1_dl.h" #include "vsp1_entity.h" +static inline struct vsp1_entity * +media_entity_to_vsp1_entity(struct media_entity *entity) +{ + return container_of(entity, struct vsp1_entity, subdev.entity); +} + void vsp1_entity_route_setup(struct vsp1_entity *source, struct vsp1_dl_list *dl) { @@ -30,7 +36,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *source, if (source->route->reg == 0) return; - sink = container_of(source->sink, struct vsp1_entity, subdev.entity); + sink = media_entity_to_vsp1_entity(source->sink); vsp1_dl_list_write(dl, source->route->reg, sink->route->inputs[source->sink_pad]); } @@ -81,12 +87,30 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad); } +/** + * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity + * @entity: the entity + * @cfg: the configuration storage + * @pad: the pad number + * @target: the selection target + * + * Return the selection rectangle stored in the given configuration for an + * entity's pad. The configuration can be an ACTIVE or TRY configuration. The + * selection target can be COMPOSE or CROP. + */ struct v4l2_rect * -vsp1_entity_get_pad_compose(struct vsp1_entity *entity, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad) +vsp1_entity_get_pad_selection(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, unsigned int target) { - return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad); + switch (target) { + case V4L2_SEL_TGT_COMPOSE: + return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad); + case V4L2_SEL_TGT_CROP: + return v4l2_subdev_get_try_crop(&entity->subdev, cfg, pad); + default: + return NULL; + } } /* @@ -252,7 +276,7 @@ int vsp1_entity_link_setup(struct media_entity *entity, if (!(local->flags & MEDIA_PAD_FL_SOURCE)) return 0; - source = container_of(local->entity, struct vsp1_entity, subdev.entity); + source = media_entity_to_vsp1_entity(local->entity); if (!source->route) return 0; @@ -274,33 +298,50 @@ int vsp1_entity_link_setup(struct media_entity *entity, * Initialization */ +#define VSP1_ENTITY_ROUTE(ent) \ + { VSP1_ENTITY_##ent, 0, VI6_DPR_##ent##_ROUTE, \ + { VI6_DPR_NODE_##ent }, VI6_DPR_NODE_##ent } + +#define VSP1_ENTITY_ROUTE_RPF(idx) \ + { VSP1_ENTITY_RPF, idx, VI6_DPR_RPF_ROUTE(idx), \ + { 0, }, VI6_DPR_NODE_RPF(idx) } + +#define VSP1_ENTITY_ROUTE_UDS(idx) \ + { VSP1_ENTITY_UDS, idx, VI6_DPR_UDS_ROUTE(idx), \ + { VI6_DPR_NODE_UDS(idx) }, VI6_DPR_NODE_UDS(idx) } + +#define VSP1_ENTITY_ROUTE_WPF(idx) \ + { VSP1_ENTITY_WPF, idx, 0, \ + { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) } + static const struct vsp1_route vsp1_routes[] = { { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE, { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), - VI6_DPR_NODE_BRU_IN(4) } }, - { VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } }, - { VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } }, - { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } }, - { VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } }, - { VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { 0, } }, - { VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { 0, } }, - { VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { 0, } }, - { VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { 0, } }, - { VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { 0, } }, - { VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } }, - { VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } }, - { VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } }, - { VSP1_ENTITY_UDS, 2, VI6_DPR_UDS_ROUTE(2), { VI6_DPR_NODE_UDS(2), } }, - { VSP1_ENTITY_WPF, 0, 0, { VI6_DPR_NODE_WPF(0), } }, - { VSP1_ENTITY_WPF, 1, 0, { VI6_DPR_NODE_WPF(1), } }, - { VSP1_ENTITY_WPF, 2, 0, { VI6_DPR_NODE_WPF(2), } }, - { VSP1_ENTITY_WPF, 3, 0, { VI6_DPR_NODE_WPF(3), } }, + VI6_DPR_NODE_BRU_IN(4) }, VI6_DPR_NODE_BRU_OUT }, + VSP1_ENTITY_ROUTE(CLU), + VSP1_ENTITY_ROUTE(HSI), + VSP1_ENTITY_ROUTE(HST), + { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, }, VI6_DPR_NODE_LIF }, + VSP1_ENTITY_ROUTE(LUT), + VSP1_ENTITY_ROUTE_RPF(0), + VSP1_ENTITY_ROUTE_RPF(1), + VSP1_ENTITY_ROUTE_RPF(2), + VSP1_ENTITY_ROUTE_RPF(3), + VSP1_ENTITY_ROUTE_RPF(4), + VSP1_ENTITY_ROUTE(SRU), + VSP1_ENTITY_ROUTE_UDS(0), + VSP1_ENTITY_ROUTE_UDS(1), + VSP1_ENTITY_ROUTE_UDS(2), + VSP1_ENTITY_ROUTE_WPF(0), + VSP1_ENTITY_ROUTE_WPF(1), + VSP1_ENTITY_ROUTE_WPF(2), + VSP1_ENTITY_ROUTE_WPF(3), }; int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, const char *name, unsigned int num_pads, - const struct v4l2_subdev_ops *ops) + const struct v4l2_subdev_ops *ops, u32 function) { struct v4l2_subdev *subdev; unsigned int i; @@ -341,6 +382,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, subdev = &entity->subdev; v4l2_subdev_init(subdev, ops); + subdev->entity.function = function; subdev->entity.ops = &vsp1->media_ops; subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 69eff4e17350..b43457fd2c43 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -24,6 +24,7 @@ struct vsp1_pipeline; enum vsp1_entity_type { VSP1_ENTITY_BRU, + VSP1_ENTITY_CLU, VSP1_ENTITY_HSI, VSP1_ENTITY_HST, VSP1_ENTITY_LIF, @@ -42,17 +43,21 @@ enum vsp1_entity_type { * @index: Entity index this routing entry is associated with * @reg: Output routing configuration register * @inputs: Target node value for each input + * @output: Target node value for entity output * * Each $vsp1_route entry describes routing configuration for the entity * specified by the entry's @type and @index. @reg indicates the register that * holds output routing configuration for the entity, and the @inputs array - * store the target node value for each input of the entity. + * store the target node value for each input of the entity. The @output field + * stores the target node value of the entity output when used as a source for + * histogram generation. */ struct vsp1_route { enum vsp1_entity_type type; unsigned int index; unsigned int reg; unsigned int inputs[VSP1_ENTITY_MAX_INPUTS]; + unsigned int output; }; /** @@ -68,7 +73,7 @@ struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *); void (*set_memory)(struct vsp1_entity *, struct vsp1_dl_list *dl); void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *); + struct vsp1_dl_list *, bool); }; struct vsp1_entity { @@ -100,7 +105,7 @@ static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, const char *name, unsigned int num_pads, - const struct v4l2_subdev_ops *ops); + const struct v4l2_subdev_ops *ops, u32 function); void vsp1_entity_destroy(struct vsp1_entity *entity); extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops; @@ -118,9 +123,9 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_pad_config *cfg, unsigned int pad); struct v4l2_rect * -vsp1_entity_get_pad_compose(struct vsp1_entity *entity, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad); +vsp1_entity_get_pad_selection(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, unsigned int target); int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg); diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 68b8567b374d..6e5077beb38c 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -107,7 +107,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_subdev_pad_ops hsit_pad_ops = { +static const struct v4l2_subdev_pad_ops hsit_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = hsit_enum_mbus_code, .enum_frame_size = hsit_enum_frame_size, @@ -115,7 +115,7 @@ static struct v4l2_subdev_pad_ops hsit_pad_ops = { .set_fmt = hsit_set_format, }; -static struct v4l2_subdev_ops hsit_ops = { +static const struct v4l2_subdev_ops hsit_ops = { .pad = &hsit_pad_ops, }; @@ -125,10 +125,13 @@ static struct v4l2_subdev_ops hsit_ops = { static void hsit_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_hsit *hsit = to_hsit(&entity->subdev); + if (!full) + return; + if (hsit->inverse) vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); else @@ -161,8 +164,9 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) else hsit->entity.type = VSP1_ENTITY_HST; - ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst", 2, - &hsit_ops); + ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst", + 2, &hsit_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 0217393f22df..a720063f38c5 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -104,7 +104,7 @@ static int lif_set_format(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_subdev_pad_ops lif_pad_ops = { +static const struct v4l2_subdev_pad_ops lif_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lif_enum_mbus_code, .enum_frame_size = lif_enum_frame_size, @@ -112,7 +112,7 @@ static struct v4l2_subdev_pad_ops lif_pad_ops = { .set_fmt = lif_set_format, }; -static struct v4l2_subdev_ops lif_ops = { +static const struct v4l2_subdev_ops lif_ops = { .pad = &lif_pad_ops, }; @@ -122,7 +122,7 @@ static struct v4l2_subdev_ops lif_ops = { static void lif_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { const struct v4l2_mbus_framefmt *format; struct vsp1_lif *lif = to_lif(&entity->subdev); @@ -130,6 +130,9 @@ static void lif_configure(struct vsp1_entity *entity, unsigned int obth = 400; unsigned int lbth = 200; + if (!full) + return; + format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, LIF_PAD_SOURCE); @@ -165,7 +168,12 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) lif->entity.ops = &lif_entity_ops; lif->entity.type = VSP1_ENTITY_LIF; - ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops); + /* The LIF is never exposed to userspace, but media entity registration + * requires a function to be set. Use PROC_VIDEO_PIXEL_FORMATTER just to + * avoid triggering a WARN_ON(), the value won't be seen anywhere. + */ + ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index aa09e59f0ab8..dc31de9602ba 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -13,7 +13,6 @@ #include <linux/device.h> #include <linux/gfp.h> -#include <linux/vsp1.h> #include <media/v4l2-subdev.h> @@ -35,43 +34,62 @@ static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl, } /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations + * Controls */ -static int lut_set_table(struct vsp1_lut *lut, struct vsp1_lut_config *config) +#define V4L2_CID_VSP1_LUT_TABLE (V4L2_CID_USER_BASE | 0x1001) + +static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) { struct vsp1_dl_body *dlb; unsigned int i; - dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, ARRAY_SIZE(config->lut)); + dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, 256); if (!dlb) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(config->lut); ++i) + for (i = 0; i < 256; ++i) vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i, - config->lut[i]); + ctrl->p_new.p_u32[i]); - mutex_lock(&lut->lock); + spin_lock_irq(&lut->lock); swap(lut->lut, dlb); - mutex_unlock(&lut->lock); + spin_unlock_irq(&lut->lock); vsp1_dl_fragment_free(dlb); return 0; } -static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg) +static int lut_s_ctrl(struct v4l2_ctrl *ctrl) { - struct vsp1_lut *lut = to_lut(subdev); - - switch (cmd) { - case VIDIOC_VSP1_LUT_CONFIG: - return lut_set_table(lut, arg); + struct vsp1_lut *lut = + container_of(ctrl->handler, struct vsp1_lut, ctrls); - default: - return -ENOIOCTLCMD; + switch (ctrl->id) { + case V4L2_CID_VSP1_LUT_TABLE: + lut_set_table(lut, ctrl); + break; } + + return 0; } +static const struct v4l2_ctrl_ops lut_ctrl_ops = { + .s_ctrl = lut_s_ctrl, +}; + +static const struct v4l2_ctrl_config lut_table_control = { + .ops = &lut_ctrl_ops, + .id = V4L2_CID_VSP1_LUT_TABLE, + .name = "Look-Up Table", + .type = V4L2_CTRL_TYPE_U32, + .min = 0x00000000, + .max = 0x00ffffff, + .step = 1, + .def = 0, + .dims = { 256}, +}; + /* ----------------------------------------------------------------------------- * V4L2 Subdevice Pad Operations */ @@ -147,11 +165,7 @@ static int lut_set_format(struct v4l2_subdev *subdev, * V4L2 Subdevice Operations */ -static struct v4l2_subdev_core_ops lut_core_ops = { - .ioctl = lut_ioctl, -}; - -static struct v4l2_subdev_pad_ops lut_pad_ops = { +static const struct v4l2_subdev_pad_ops lut_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lut_enum_mbus_code, .enum_frame_size = lut_enum_frame_size, @@ -159,8 +173,7 @@ static struct v4l2_subdev_pad_ops lut_pad_ops = { .set_fmt = lut_set_format, }; -static struct v4l2_subdev_ops lut_ops = { - .core = &lut_core_ops, +static const struct v4l2_subdev_ops lut_ops = { .pad = &lut_pad_ops, }; @@ -170,18 +183,24 @@ static struct v4l2_subdev_ops lut_ops = { static void lut_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_lut *lut = to_lut(&entity->subdev); + struct vsp1_dl_body *dlb; + unsigned long flags; - vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); - - mutex_lock(&lut->lock); - if (lut->lut) { - vsp1_dl_list_add_fragment(dl, lut->lut); - lut->lut = NULL; + if (full) { + vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); + return; } - mutex_unlock(&lut->lock); + + spin_lock_irqsave(&lut->lock, flags); + dlb = lut->lut; + lut->lut = NULL; + spin_unlock_irqrestore(&lut->lock, flags); + + if (dlb) + vsp1_dl_list_add_fragment(dl, dlb); } static const struct vsp1_entity_operations lut_entity_ops = { @@ -201,12 +220,30 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) if (lut == NULL) return ERR_PTR(-ENOMEM); + spin_lock_init(&lut->lock); + lut->entity.ops = &lut_entity_ops; lut->entity.type = VSP1_ENTITY_LUT; - ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops); + ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops, + MEDIA_ENT_F_PROC_VIDEO_LUT); if (ret < 0) return ERR_PTR(ret); + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&lut->ctrls, 1); + v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL); + + lut->entity.subdev.ctrl_handler = &lut->ctrls; + + if (lut->ctrls.error) { + dev_err(vsp1->dev, "lut: failed to initialize controls\n"); + ret = lut->ctrls.error; + vsp1_entity_destroy(&lut->entity); + return ERR_PTR(ret); + } + + v4l2_ctrl_handler_setup(&lut->ctrls); + return lut; } diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h index cef874f22b6a..f8c4e8f0a79d 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.h +++ b/drivers/media/platform/vsp1/vsp1_lut.h @@ -13,9 +13,10 @@ #ifndef __VSP1_LUT_H__ #define __VSP1_LUT_H__ -#include <linux/mutex.h> +#include <linux/spinlock.h> #include <media/media-entity.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-subdev.h> #include "vsp1_entity.h" @@ -28,7 +29,9 @@ struct vsp1_device; struct vsp1_lut { struct vsp1_entity entity; - struct mutex lock; + struct v4l2_ctrl_handler ctrls; + + spinlock_t lock; struct vsp1_dl_body *lut; }; diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 4f3b4a1d028a..3e75fb3fcace 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -172,13 +172,17 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) bru->inputs[i].rpf = NULL; } - for (i = 0; i < pipe->num_inputs; ++i) { - pipe->inputs[i]->pipe = NULL; - pipe->inputs[i] = NULL; + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { + if (pipe->inputs[i]) { + pipe->inputs[i]->pipe = NULL; + pipe->inputs[i] = NULL; + } } - pipe->output->pipe = NULL; - pipe->output = NULL; + if (pipe->output) { + pipe->output->pipe = NULL; + pipe->output = NULL; + } INIT_LIST_HEAD(&pipe->entities); pipe->state = VSP1_PIPELINE_STOPPED; @@ -286,6 +290,8 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) if (pipe->frame_end) pipe->frame_end(pipe); + + pipe->sequence++; } /* @@ -295,42 +301,20 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha * value. The UDS then outputs a fixed alpha value which needs to be programmed * from the input RPF alpha. - * - * This function can only be called from a subdev s_stream handler as it - * requires a valid display list context. */ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, - struct vsp1_entity *input, - struct vsp1_dl_list *dl, - unsigned int alpha) + struct vsp1_dl_list *dl, unsigned int alpha) { - struct vsp1_entity *entity; - struct media_pad *pad; - - pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); - - while (pad) { - if (!is_media_entity_v4l2_subdev(pad->entity)) - break; - - entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); - - /* The BRU background color has a fixed alpha value set to 255, - * the output alpha value is thus always equal to 255. - */ - if (entity->type == VSP1_ENTITY_BRU) - alpha = 255; - - if (entity->type == VSP1_ENTITY_UDS) { - struct vsp1_uds *uds = to_uds(&entity->subdev); + if (!pipe->uds) + return; - vsp1_uds_set_alpha(uds, dl, alpha); - break; - } + /* The BRU background color has a fixed alpha value set to 255, the + * output alpha value is thus always equal to 255. + */ + if (pipe->uds_input->type == VSP1_ENTITY_BRU) + alpha = 255; - pad = &entity->pads[entity->source_pad]; - pad = media_entity_remote_pad(pad); - } + vsp1_uds_set_alpha(pipe->uds, dl, alpha); } void vsp1_pipelines_suspend(struct vsp1_device *vsp1) @@ -383,7 +367,7 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1) { unsigned int i; - /* Resume pipeline all running pipelines. */ + /* Resume all running pipelines. */ for (i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; struct vsp1_pipeline *pipe; diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index 7b56113511dd..d20d997b1fda 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -61,12 +61,13 @@ enum vsp1_pipeline_state { * @pipe: the media pipeline * @irqlock: protects the pipeline state * @state: current state - * @wq: work queue to wait for state change completion + * @wq: wait queue to wait for state change completion * @frame_end: frame end interrupt handler * @lock: protects the pipeline use count and stream count * @kref: pipeline reference count * @stream_count: number of streaming video nodes * @buffers_ready: bitmask of RPFs and WPFs with at least one buffer available + * @sequence: frame sequence number * @num_inputs: number of RPFs * @inputs: array of RPFs in the pipeline (indexed by RPF index) * @output: WPF at the output of the pipeline @@ -90,6 +91,7 @@ struct vsp1_pipeline { struct kref kref; unsigned int stream_count; unsigned int buffers_ready; + unsigned int sequence; unsigned int num_inputs; struct vsp1_rwpf *inputs[VSP1_MAX_RPF]; @@ -115,9 +117,7 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe); void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, - struct vsp1_entity *input, - struct vsp1_dl_list *dl, - unsigned int alpha); + struct vsp1_dl_list *dl, unsigned int alpha); void vsp1_pipelines_suspend(struct vsp1_device *vsp1); void vsp1_pipelines_resume(struct vsp1_device *vsp1); diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 927b5fb94c48..3b03007ba625 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -154,10 +154,10 @@ #define VI6_RPF_ALPH_SEL_AEXT_EXT (1 << 18) #define VI6_RPF_ALPH_SEL_AEXT_ONE (2 << 18) #define VI6_RPF_ALPH_SEL_AEXT_MASK (3 << 18) -#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 8) -#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 8 -#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 0) -#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 0 +#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 8) +#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 8 +#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 0) +#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 0 #define VI6_RPF_VRTCOL_SET 0x0318 #define VI6_RPF_VRTCOL_SET_LAYA_MASK (0xff << 24) @@ -255,6 +255,8 @@ #define VI6_WPF_OUTFMT_PDV_MASK (0xff << 24) #define VI6_WPF_OUTFMT_PDV_SHIFT 24 #define VI6_WPF_OUTFMT_PXA (1 << 23) +#define VI6_WPF_OUTFMT_ROT (1 << 18) +#define VI6_WPF_OUTFMT_HFLP (1 << 17) #define VI6_WPF_OUTFMT_FLP (1 << 16) #define VI6_WPF_OUTFMT_SPYCS (1 << 15) #define VI6_WPF_OUTFMT_SPUVS (1 << 14) @@ -289,6 +291,11 @@ #define VI6_WPF_RNDCTRL_CLMD_EXT (2 << 12) #define VI6_WPF_RNDCTRL_CLMD_MASK (3 << 12) +#define VI6_WPF_ROT_CTRL 0x1018 +#define VI6_WPF_ROT_CTRL_LN16 (1 << 17) +#define VI6_WPF_ROT_CTRL_LMEM_WD_MASK (0x1fff << 0) +#define VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT 0 + #define VI6_WPF_DSTM_STRIDE_Y 0x101c #define VI6_WPF_DSTM_STRIDE_C 0x1020 #define VI6_WPF_DSTM_ADDR_Y 0x1024 @@ -444,6 +451,15 @@ */ #define VI6_CLU_CTRL 0x2900 +#define VI6_CLU_CTRL_AAI (1 << 28) +#define VI6_CLU_CTRL_MVS (1 << 24) +#define VI6_CLU_CTRL_AX1I_2D (3 << 14) +#define VI6_CLU_CTRL_AX2I_2D (1 << 12) +#define VI6_CLU_CTRL_OS0_2D (3 << 8) +#define VI6_CLU_CTRL_OS1_2D (1 << 6) +#define VI6_CLU_CTRL_OS2_2D (3 << 4) +#define VI6_CLU_CTRL_M2D (1 << 1) +#define VI6_CLU_CTRL_EN (1 << 0) /* ----------------------------------------------------------------------------- * HST Control Registers diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 49168db3f529..388838913205 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -38,7 +38,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, * V4L2 Subdevice Operations */ -static struct v4l2_subdev_ops rpf_ops = { +static const struct v4l2_subdev_ops rpf_ops = { .pad = &vsp1_rwpf_pad_ops, }; @@ -60,7 +60,7 @@ static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) static void rpf_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; @@ -73,6 +73,16 @@ static void rpf_configure(struct vsp1_entity *entity, u32 pstride; u32 infmt; + if (!full) { + vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, + rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | + (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT)); + + vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha); + return; + } + /* Source size, stride and crop offsets. * * The crop offsets correspond to the location of the crop rectangle top @@ -130,9 +140,10 @@ static void rpf_configure(struct vsp1_entity *entity, if (pipe->bru) { const struct v4l2_rect *compose; - compose = vsp1_entity_get_pad_compose(pipe->bru, - pipe->bru->config, - rpf->bru_input); + compose = vsp1_entity_get_pad_selection(pipe->bru, + pipe->bru->config, + rpf->bru_input, + V4L2_SEL_TGT_COMPOSE); left = compose->left; top = compose->top; } @@ -167,9 +178,6 @@ static void rpf_configure(struct vsp1_entity *entity, (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED : VI6_RPF_ALPH_SEL_ASEL_FIXED)); - vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, - rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); - if (entity->vsp1->info->gen == 3) { u32 mult; @@ -187,8 +195,7 @@ static void rpf_configure(struct vsp1_entity *entity, mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO | (premultiplied ? VI6_RPF_MULT_ALPHA_P_MMD_RATIO : - VI6_RPF_MULT_ALPHA_P_MMD_NONE) - | (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT); + VI6_RPF_MULT_ALPHA_P_MMD_NONE); } else { /* When the input doesn't contain an alpha channel the * global alpha value is applied in the unpacking unit, @@ -199,11 +206,9 @@ static void rpf_configure(struct vsp1_entity *entity, | VI6_RPF_MULT_ALPHA_P_MMD_NONE; } - vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, mult); + rpf->mult_alpha = mult; } - vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, dl, rpf->alpha); - vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0); vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0); @@ -236,18 +241,21 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) rpf->entity.index = index; sprintf(name, "rpf.%u", index); - ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops); + ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); if (ret < 0) return ERR_PTR(ret); /* Initialize the control handler. */ - ret = vsp1_rwpf_init_ctrls(rpf); + ret = vsp1_rwpf_init_ctrls(rpf, 0); if (ret < 0) { dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n", index); goto error; } + v4l2_ctrl_handler_setup(&rpf->ctrls); + return rpf; error: diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 3b6e032e7806..8d461b375e91 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -241,11 +241,9 @@ static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = { .s_ctrl = vsp1_rwpf_s_ctrl, }; -int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf) +int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols) { - rwpf->alpha = 255; - - v4l2_ctrl_handler_init(&rwpf->ctrls, 1); + v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1); v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 9ff7c78f239e..cb20484e80da 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -13,6 +13,8 @@ #ifndef __VSP1_RWPF_H__ #define __VSP1_RWPF_H__ +#include <linux/spinlock.h> + #include <media/media-entity.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-subdev.h> @@ -49,6 +51,16 @@ struct vsp1_rwpf { unsigned int alpha; + u32 mult_alpha; + u32 outfmt; + + struct { + spinlock_t lock; + struct v4l2_ctrl *ctrls[2]; + unsigned int pending; + unsigned int active; + } flip; + unsigned int offsets[2]; struct vsp1_rwpf_memory mem; @@ -68,7 +80,7 @@ static inline struct vsp1_rwpf *entity_to_rwpf(struct vsp1_entity *entity) struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); -int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf); +int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols); extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 97ef997ae735..47f5e0cea2ce 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -37,7 +37,7 @@ static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl, * Controls */ -#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE + 1) +#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE | 0x1001) struct vsp1_sru_param { u32 ctrl0; @@ -239,7 +239,7 @@ static int sru_set_format(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_subdev_pad_ops sru_pad_ops = { +static const struct v4l2_subdev_pad_ops sru_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = sru_enum_mbus_code, .enum_frame_size = sru_enum_frame_size, @@ -247,7 +247,7 @@ static struct v4l2_subdev_pad_ops sru_pad_ops = { .set_fmt = sru_set_format, }; -static struct v4l2_subdev_ops sru_ops = { +static const struct v4l2_subdev_ops sru_ops = { .pad = &sru_pad_ops, }; @@ -257,7 +257,7 @@ static struct v4l2_subdev_ops sru_ops = { static void sru_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { const struct vsp1_sru_param *param; struct vsp1_sru *sru = to_sru(&entity->subdev); @@ -265,6 +265,9 @@ static void sru_configure(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; + if (!full) + return; + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, SRU_PAD_SINK); output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, @@ -308,7 +311,8 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) sru->entity.ops = &sru_entity_ops; sru->entity.type = VSP1_ENTITY_SRU; - ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops); + ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops, + MEDIA_ENT_F_PROC_VIDEO_SCALER); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 1875e29da184..652dcd895022 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -40,9 +40,11 @@ static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl, * Scaling Computation */ -void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl, +void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_list *dl, unsigned int alpha) { + struct vsp1_uds *uds = to_uds(&entity->subdev); + vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT); } @@ -226,7 +228,7 @@ static int uds_set_format(struct v4l2_subdev *subdev, * V4L2 Subdevice Operations */ -static struct v4l2_subdev_pad_ops uds_pad_ops = { +static const struct v4l2_subdev_pad_ops uds_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uds_enum_mbus_code, .enum_frame_size = uds_enum_frame_size, @@ -234,7 +236,7 @@ static struct v4l2_subdev_pad_ops uds_pad_ops = { .set_fmt = uds_set_format, }; -static struct v4l2_subdev_ops uds_ops = { +static const struct v4l2_subdev_ops uds_ops = { .pad = &uds_pad_ops, }; @@ -244,7 +246,7 @@ static struct v4l2_subdev_ops uds_ops = { static void uds_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; @@ -253,6 +255,9 @@ static void uds_configure(struct vsp1_entity *entity, unsigned int vscale; bool multitap; + if (!full) + return; + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, UDS_PAD_SINK); output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, @@ -314,7 +319,8 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) uds->entity.index = index; sprintf(name, "uds.%u", index); - ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops); + ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops, + MEDIA_ENT_F_PROC_VIDEO_SCALER); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h index 5c8cbfcad4cc..7bf3cdcffc65 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.h +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -35,7 +35,7 @@ static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index); -void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl, +void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_list *dl, unsigned int alpha); #endif /* __VSP1_UDS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index a9aec5c0bec6..01654232b695 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -219,7 +219,7 @@ vsp1_video_complete_buffer(struct vsp1_video *video) spin_unlock_irqrestore(&video->irqlock, flags); - done->buf.sequence = video->sequence++; + done->buf.sequence = pipe->sequence; done->buf.vb2_buf.timestamp = ktime_get_ns(); for (i = 0; i < done->buf.vb2_buf.num_planes; ++i) vb2_set_plane_payload(&done->buf.vb2_buf, i, @@ -251,11 +251,17 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + struct vsp1_entity *entity; unsigned int i; if (!pipe->dl) pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + list_for_each_entry(entity, &pipe->entities, list_pipe) { + if (entity->ops->configure) + entity->ops->configure(entity, pipe, pipe->dl, false); + } + for (i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *rwpf = pipe->inputs[i]; @@ -632,7 +638,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) vsp1_entity_route_setup(entity, pipe->dl); if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl); + entity->ops->configure(entity, pipe, pipe->dl, true); } return 0; @@ -674,7 +680,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) int ret; mutex_lock(&pipe->lock); - if (--pipe->stream_count == 0) { + if (--pipe->stream_count == pipe->num_inputs) { /* Stop the pipeline. */ ret = vsp1_pipeline_stop(pipe); if (ret == -ETIMEDOUT) @@ -696,7 +702,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) spin_unlock_irqrestore(&video->irqlock, flags); } -static struct vb2_ops vsp1_video_queue_qops = { +static const struct vb2_ops vsp1_video_queue_qops = { .queue_setup = vsp1_video_queue_setup, .buf_prepare = vsp1_video_buffer_prepare, .buf_queue = vsp1_video_buffer_queue, @@ -805,8 +811,6 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (video->queue.owner && video->queue.owner != file->private_data) return -EBUSY; - video->sequence = 0; - /* Get a pipeline for the video node and start streaming on it. No link * touching an entity in the pipeline can be activated or deactivated * once streaming is started. @@ -915,7 +919,7 @@ static int vsp1_video_release(struct file *file) return 0; } -static struct v4l2_file_operations vsp1_video_fops = { +static const struct v4l2_file_operations vsp1_video_fops = { .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, .open = vsp1_video_open, diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 867b00807c46..1595fd587fbc 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -49,7 +49,6 @@ struct vsp1_video { void *alloc_ctx; spinlock_t irqlock; struct list_head irqqueue; - unsigned int sequence; }; static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev) diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 6c91eaa35e75..31983169c24a 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -37,6 +37,97 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, } /* ----------------------------------------------------------------------------- + * Controls + */ + +enum wpf_flip_ctrl { + WPF_CTRL_VFLIP = 0, + WPF_CTRL_HFLIP = 1, + WPF_CTRL_MAX, +}; + +static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_rwpf *wpf = + container_of(ctrl->handler, struct vsp1_rwpf, ctrls); + unsigned int i; + u32 flip = 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + for (i = 0; i < WPF_CTRL_MAX; ++i) { + if (wpf->flip.ctrls[i]) + flip |= wpf->flip.ctrls[i]->val ? BIT(i) : 0; + } + + spin_lock_irq(&wpf->flip.lock); + wpf->flip.pending = flip; + spin_unlock_irq(&wpf->flip.lock); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops = { + .s_ctrl = vsp1_wpf_s_ctrl, +}; + +static int wpf_init_controls(struct vsp1_rwpf *wpf) +{ + struct vsp1_device *vsp1 = wpf->entity.vsp1; + unsigned int num_flip_ctrls; + + spin_lock_init(&wpf->flip.lock); + + if (wpf->entity.index != 0) { + /* Only WPF0 supports flipping. */ + num_flip_ctrls = 0; + } else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) { + /* When horizontal flip is supported the WPF implements two + * controls (horizontal flip and vertical flip). + */ + num_flip_ctrls = 2; + } else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) { + /* When only vertical flip is supported the WPF implements a + * single control (vertical flip). + */ + num_flip_ctrls = 1; + } else { + /* Otherwise flipping is not supported. */ + num_flip_ctrls = 0; + } + + vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); + + if (num_flip_ctrls >= 1) { + wpf->flip.ctrls[WPF_CTRL_VFLIP] = + v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + } + + if (num_flip_ctrls == 2) { + wpf->flip.ctrls[WPF_CTRL_HFLIP] = + v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + v4l2_ctrl_cluster(2, wpf->flip.ctrls); + } + + if (wpf->ctrls.error) { + dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", + wpf->entity.index); + return wpf->ctrls.error; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ @@ -62,11 +153,11 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) * V4L2 Subdevice Operations */ -static struct v4l2_subdev_video_ops wpf_video_ops = { +static const struct v4l2_subdev_video_ops wpf_video_ops = { .s_stream = wpf_s_stream, }; -static struct v4l2_subdev_ops wpf_ops = { +static const struct v4l2_subdev_ops wpf_ops = { .video = &wpf_video_ops, .pad = &vsp1_rwpf_pad_ops, }; @@ -85,15 +176,37 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity) static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { struct vsp1_rwpf *wpf = entity_to_rwpf(entity); + const struct v4l2_pix_format_mplane *format = &wpf->format; + struct vsp1_rwpf_memory mem = wpf->mem; + unsigned int flip = wpf->flip.active; + unsigned int offset; + + /* Update the memory offsets based on flipping configuration. The + * destination addresses point to the locations where the VSP starts + * writing to memory, which can be different corners of the image + * depending on vertical flipping. Horizontal flipping is handled + * through a line buffer and doesn't modify the start address. + */ + if (flip & BIT(WPF_CTRL_VFLIP)) { + mem.addr[0] += (format->height - 1) + * format->plane_fmt[0].bytesperline; + + if (format->num_planes > 1) { + offset = (format->height / wpf->fmtinfo->vsub - 1) + * format->plane_fmt[1].bytesperline; + mem.addr[1] += offset; + mem.addr[2] += offset; + } + } - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, wpf->mem.addr[0]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, wpf->mem.addr[1]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); } static void wpf_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; @@ -104,6 +217,26 @@ static void wpf_configure(struct vsp1_entity *entity, u32 outfmt = 0; u32 srcrpf = 0; + if (!full) { + const unsigned int mask = BIT(WPF_CTRL_VFLIP) + | BIT(WPF_CTRL_HFLIP); + + spin_lock(&wpf->flip.lock); + wpf->flip.active = (wpf->flip.active & ~mask) + | (wpf->flip.pending & mask); + spin_unlock(&wpf->flip.lock); + + outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; + + if (wpf->flip.active & BIT(WPF_CTRL_VFLIP)) + outfmt |= VI6_WPF_OUTFMT_FLP; + if (wpf->flip.active & BIT(WPF_CTRL_HFLIP)) + outfmt |= VI6_WPF_OUTFMT_HFLP; + + vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); + return; + } + /* Cropping */ crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); @@ -143,13 +276,18 @@ static void wpf_configure(struct vsp1_entity *entity, format->plane_fmt[1].bytesperline); vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap); + + if (vsp1->info->features & VSP1_HAS_WPF_HFLIP && + wpf->entity.index == 0) + vsp1_wpf_write(wpf, dl, VI6_WPF_ROT_CTRL, + VI6_WPF_ROT_CTRL_LN16 | + (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT)); } if (sink_format->code != source_format->code) outfmt |= VI6_WPF_OUTFMT_CSC; - outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT; - vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); + wpf->outfmt = outfmt; vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index), VI6_DPR_WPF_FPORCH_FP_WPFN); @@ -216,7 +354,8 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) wpf->entity.index = index; sprintf(name, "wpf.%u", index); - ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops); + ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); if (ret < 0) return ERR_PTR(ret); @@ -228,13 +367,15 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) } /* Initialize the control handler. */ - ret = vsp1_rwpf_init_ctrls(wpf); + ret = wpf_init_controls(wpf); if (ret < 0) { dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", index); goto error; } + v4l2_ctrl_handler_setup(&wpf->ctrls); + return wpf; error: diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c index 6457ac91ef09..7f0b9d5940db 100644 --- a/drivers/media/tuners/mt2063.c +++ b/drivers/media/tuners/mt2063.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/videodev2.h> +#include <linux/gcd.h> #include "mt2063.h" @@ -665,27 +666,6 @@ static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info) } /** - * gcd() - Uses Euclid's algorithm - * - * @u, @v: Unsigned values whose GCD is desired. - * - * Returns THE greatest common divisor of u and v, if either value is 0, - * the other value is returned as the result. - */ -static u32 MT2063_gcd(u32 u, u32 v) -{ - u32 r; - - while (v != 0) { - r = u % v; - u = v; - v = r; - } - - return u; -} - -/** * IsSpurInBand() - Checks to see if a spur will be present within the IF's * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW) * @@ -731,12 +711,12 @@ static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info, ** of f_LO1, f_LO2 and the edge value. Use the larger of this ** gcd-based scale factor or f_Scale. */ - lo_gcd = MT2063_gcd(f_LO1, f_LO2); - gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale); + lo_gcd = gcd(f_LO1, f_LO2); + gd_Scale = max((u32) gcd(lo_gcd, d), f_Scale); hgds = gd_Scale / 2; - gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale); + gc_Scale = max((u32) gcd(lo_gcd, c), f_Scale); hgcs = gc_Scale / 2; - gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale); + gf_Scale = max((u32) gcd(lo_gcd, f), f_Scale); hgfs = gf_Scale / 2; n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2); diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 321ea5cf1329..bf53553d2624 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -142,7 +142,7 @@ static void au0828_unregister_media_device(struct au0828_dev *dev) struct media_device *mdev = dev->media_dev; struct media_entity_notify *notify, *nextp; - if (!mdev || !media_devnode_is_registered(&mdev->devnode)) + if (!mdev || !media_devnode_is_registered(mdev->devnode)) return; /* Remove au0828 entity_notify callbacks */ @@ -482,7 +482,7 @@ static int au0828_media_device_register(struct au0828_dev *dev, if (!dev->media_dev) return 0; - if (!media_devnode_is_registered(&dev->media_dev->devnode)) { + if (!media_devnode_is_registered(dev->media_dev->devnode)) { /* register media device */ ret = media_device_register(dev->media_dev); diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 3dc8ef004f8b..524533d3eb29 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -127,17 +127,22 @@ config DVB_USB_MXL111SF config DVB_USB_RTL28XXU tristate "Realtek RTL28xxU DVB USB support" depends on DVB_USB_V2 && I2C_MUX + select DVB_MN88472 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MN88473 if MEDIA_SUBDRV_AUTOSELECT select DVB_RTL2830 select DVB_RTL2832 select DVB_RTL2832_SDR if (MEDIA_SUBDRV_AUTOSELECT && MEDIA_SDR_SUPPORT) - select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_R820T if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Realtek RTL28xxU DVB USB receiver. diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 2638e3251f2a..eabede44ad88 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -49,6 +49,7 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) #define CHECKSUM_LEN 2 #define USB_TIMEOUT 2000 struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret, wlen, rlen; u16 checksum, tmp_checksum; @@ -57,8 +58,8 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) /* buffer overflow check */ if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) { - dev_err(&d->udev->dev, "%s: too much data wlen=%d rlen=%d\n", - KBUILD_MODNAME, req->wlen, req->rlen); + dev_err(&intf->dev, "too much data wlen=%d rlen=%d\n", + req->wlen, req->rlen); ret = -EINVAL; goto exit; } @@ -94,10 +95,8 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) checksum = af9035_checksum(state->buf, rlen - 2); tmp_checksum = (state->buf[rlen - 2] << 8) | state->buf[rlen - 1]; if (tmp_checksum != checksum) { - dev_err(&d->udev->dev, - "%s: command=%02x checksum mismatch (%04x != %04x)\n", - KBUILD_MODNAME, req->cmd, tmp_checksum, - checksum); + dev_err(&intf->dev, "command=%02x checksum mismatch (%04x != %04x)\n", + req->cmd, tmp_checksum, checksum); ret = -EIO; goto exit; } @@ -110,8 +109,8 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) goto exit; } - dev_dbg(&d->udev->dev, "%s: command=%02x failed fw error=%d\n", - __func__, req->cmd, state->buf[2]); + dev_dbg(&intf->dev, "command=%02x failed fw error=%d\n", + req->cmd, state->buf[2]); ret = -EIO; goto exit; } @@ -122,20 +121,20 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) exit: mutex_unlock(&d->usb_mutex); if (ret < 0) - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } /* write multiple registers */ static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) { + struct usb_interface *intf = d->intf; u8 wbuf[MAX_XFER_SIZE]; u8 mbox = (reg >> 16) & 0xff; struct usb_req req = { CMD_MEM_WR, mbox, 6 + len, wbuf, 0, NULL }; if (6 + len > sizeof(wbuf)) { - dev_warn(&d->udev->dev, "%s: i2c wr: len=%d is too big!\n", - KBUILD_MODNAME, len); + dev_warn(&intf->dev, "i2c wr: len=%d is too big!\n", len); return -EOPNOTSUPP; } @@ -198,6 +197,7 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type, { int ret, num; struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; struct i2c_client *client; struct i2c_board_info board_info = { .addr = addr, @@ -212,11 +212,10 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type, break; } - dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num); + dev_dbg(&intf->dev, "num=%d\n", num); if (num == AF9035_I2C_CLIENT_MAX) { - dev_err(&d->udev->dev, "%s: I2C client out of index\n", - KBUILD_MODNAME); + dev_err(&intf->dev, "I2C client out of index\n"); ret = -ENODEV; goto err; } @@ -240,7 +239,7 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type, state->i2c_client[num] = client; return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -248,6 +247,7 @@ static void af9035_del_i2c_dev(struct dvb_usb_device *d) { int num; struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; struct i2c_client *client; /* find last used client */ @@ -257,11 +257,10 @@ static void af9035_del_i2c_dev(struct dvb_usb_device *d) break; } - dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num); + dev_dbg(&intf->dev, "num=%d\n", num); if (num == -1) { - dev_err(&d->udev->dev, "%s: I2C client out of index\n", - KBUILD_MODNAME); + dev_err(&intf->dev, "I2C client out of index\n"); goto err; } @@ -276,7 +275,7 @@ static void af9035_del_i2c_dev(struct dvb_usb_device *d) state->i2c_client[num] = NULL; return; err: - dev_dbg(&d->udev->dev, "%s: failed\n", __func__); + dev_dbg(&intf->dev, "failed\n"); } static int af9035_i2c_master_xfer(struct i2c_adapter *adap, @@ -348,6 +347,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = af9035_rd_regs(d, reg, &msg[1].buf[0], msg[1].len); + } else if (state->no_read) { + memset(msg[1].buf, 0, msg[1].len); + ret = 0; } else { /* I2C write + read */ u8 buf[MAX_XFER_SIZE]; @@ -367,10 +369,25 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, memcpy(&buf[3], msg[0].buf, msg[0].len); } else { buf[1] = msg[0].addr << 1; - buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ - memcpy(&buf[5], msg[0].buf, msg[0].len); + + /* Keep prev behavior for write req len > 2*/ + if (msg[0].len > 2) { + buf[2] = 0x00; /* reg addr len */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + + /* Use reg addr fields if write req len <= 2 */ + } else { + req.wlen = 5; + buf[2] = msg[0].len; + if (msg[0].len == 2) { + buf[3] = msg[0].buf[0]; + buf[4] = msg[0].buf[1]; + } else if (msg[0].len == 1) { + buf[4] = msg[0].buf[0]; + } + } } ret = af9035_ctrl_msg(d, &req); } @@ -421,6 +438,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; + } else if (state->no_read) { + memset(msg[0].buf, 0, msg[0].len); + ret = 0; } else { /* I2C read */ u8 buf[5]; @@ -475,6 +495,7 @@ static struct i2c_algorithm af9035_i2c_algo = { static int af9035_identify_state(struct dvb_usb_device *d, const char **name) { struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret; u8 wbuf[1] = { 1 }; u8 rbuf[4]; @@ -492,10 +513,8 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) if (ret < 0) goto err; - dev_info(&d->udev->dev, - "%s: prechip_version=%02x chip_version=%02x chip_type=%04x\n", - KBUILD_MODNAME, state->prechip_version, - state->chip_version, state->chip_type); + dev_info(&intf->dev, "prechip_version=%02x chip_version=%02x chip_type=%04x\n", + state->prechip_version, state->chip_version, state->chip_type); if (state->chip_type == 0x9135) { if (state->chip_version == 0x02) @@ -515,7 +534,7 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: reply=%*ph\n", __func__, 4, rbuf); + dev_dbg(&intf->dev, "reply=%*ph\n", 4, rbuf); if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3]) ret = WARM; else @@ -524,7 +543,7 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) return ret; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -532,6 +551,7 @@ err: static int af9035_download_firmware_old(struct dvb_usb_device *d, const struct firmware *fw) { + struct usb_interface *intf = d->intf; int ret, i, j, len; u8 wbuf[1]; struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; @@ -562,14 +582,12 @@ static int af9035_download_firmware_old(struct dvb_usb_device *d, hdr_checksum = fw->data[fw->size - i + 5] << 8; hdr_checksum |= fw->data[fw->size - i + 6] << 0; - dev_dbg(&d->udev->dev, - "%s: core=%d addr=%04x data_len=%d checksum=%04x\n", - __func__, hdr_core, hdr_addr, hdr_data_len, - hdr_checksum); + dev_dbg(&intf->dev, "core=%d addr=%04x data_len=%d checksum=%04x\n", + hdr_core, hdr_addr, hdr_data_len, hdr_checksum); if (((hdr_core != 1) && (hdr_core != 2)) || (hdr_data_len > i)) { - dev_dbg(&d->udev->dev, "%s: bad firmware\n", __func__); + dev_dbg(&intf->dev, "bad firmware\n"); break; } @@ -600,18 +618,17 @@ static int af9035_download_firmware_old(struct dvb_usb_device *d, i -= hdr_data_len + HDR_SIZE; - dev_dbg(&d->udev->dev, "%s: data uploaded=%zu\n", - __func__, fw->size - i); + dev_dbg(&intf->dev, "data uploaded=%zu\n", fw->size - i); } /* print warn if firmware is bad, continue and see what happens */ if (i) - dev_warn(&d->udev->dev, "%s: bad firmware\n", KBUILD_MODNAME); + dev_warn(&intf->dev, "bad firmware\n"); return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -619,6 +636,7 @@ err: static int af9035_download_firmware_new(struct dvb_usb_device *d, const struct firmware *fw) { + struct usb_interface *intf = d->intf; int ret, i, i_prev; struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL }; #define HDR_SIZE 7 @@ -648,15 +666,14 @@ static int af9035_download_firmware_new(struct dvb_usb_device *d, if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: data uploaded=%d\n", - __func__, i); + dev_dbg(&intf->dev, "data uploaded=%d\n", i); } } return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -664,6 +681,7 @@ err: static int af9035_download_firmware(struct dvb_usb_device *d, const struct firmware *fw) { + struct usb_interface *intf = d->intf; struct state *state = d_to_priv(d); int ret; u8 wbuf[1]; @@ -672,7 +690,7 @@ static int af9035_download_firmware(struct dvb_usb_device *d, struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf }; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + dev_dbg(&intf->dev, "\n"); /* * In case of dual tuner configuration we need to do some extra @@ -752,25 +770,25 @@ static int af9035_download_firmware(struct dvb_usb_device *d, goto err; if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { - dev_err(&d->udev->dev, "%s: firmware did not run\n", - KBUILD_MODNAME); + dev_err(&intf->dev, "firmware did not run\n"); ret = -ENODEV; goto err; } - dev_info(&d->udev->dev, "%s: firmware version=%d.%d.%d.%d", - KBUILD_MODNAME, rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + dev_info(&intf->dev, "firmware version=%d.%d.%d.%d", + rbuf[0], rbuf[1], rbuf[2], rbuf[3]); return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } static int af9035_read_config(struct dvb_usb_device *d) { + struct usb_interface *intf = d->intf; struct state *state = d_to_priv(d); int ret, i; u8 tmp; @@ -805,7 +823,7 @@ static int af9035_read_config(struct dvb_usb_device *d) goto err; if (tmp == 0x00) { - dev_dbg(&d->udev->dev, "%s: no eeprom\n", __func__); + dev_dbg(&intf->dev, "no eeprom\n"); goto skip_eeprom; } } else if (state->chip_type == 0x9306) { @@ -826,8 +844,7 @@ static int af9035_read_config(struct dvb_usb_device *d) if (tmp == 1 || tmp == 3 || tmp == 5) state->dual_mode = true; - dev_dbg(&d->udev->dev, "%s: ts mode=%d dual mode=%d\n", __func__, - tmp, state->dual_mode); + dev_dbg(&intf->dev, "ts mode=%d dual mode=%d\n", tmp, state->dual_mode); if (state->dual_mode) { /* read 2nd demodulator I2C address */ @@ -840,8 +857,7 @@ static int af9035_read_config(struct dvb_usb_device *d) if (tmp) state->af9033_i2c_addr[1] = tmp; - dev_dbg(&d->udev->dev, "%s: 2nd demod I2C addr=%02x\n", - __func__, tmp); + dev_dbg(&intf->dev, "2nd demod I2C addr=%02x\n", tmp); } addr = state->eeprom_addr; @@ -852,8 +868,7 @@ static int af9035_read_config(struct dvb_usb_device *d) if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: [%d]tuner=%02x\n", - __func__, i, tmp); + dev_dbg(&intf->dev, "[%d]tuner=%02x\n", i, tmp); /* tuner sanity check */ if (state->chip_type == 0x9135) { @@ -882,10 +897,8 @@ static int af9035_read_config(struct dvb_usb_device *d) } if (state->af9033_config[i].tuner != tmp) { - dev_info(&d->udev->dev, - "%s: [%d] overriding tuner from %02x to %02x\n", - KBUILD_MODNAME, i, tmp, - state->af9033_config[i].tuner); + dev_info(&intf->dev, "[%d] overriding tuner from %02x to %02x\n", + i, tmp, state->af9033_config[i].tuner); } switch (state->af9033_config[i].tuner) { @@ -905,9 +918,8 @@ static int af9035_read_config(struct dvb_usb_device *d) case AF9033_TUNER_IT9135_62: break; default: - dev_warn(&d->udev->dev, - "%s: tuner id=%02x not supported, please report!", - KBUILD_MODNAME, tmp); + dev_warn(&intf->dev, "tuner id=%02x not supported, please report!", + tmp); } /* disable dual mode if driver does not support it */ @@ -924,9 +936,7 @@ static int af9035_read_config(struct dvb_usb_device *d) break; default: state->dual_mode = false; - dev_info(&d->udev->dev, - "%s: driver does not support 2nd tuner and will disable it", - KBUILD_MODNAME); + dev_info(&intf->dev, "driver does not support 2nd tuner and will disable it"); } /* tuner IF frequency */ @@ -942,7 +952,7 @@ static int af9035_read_config(struct dvb_usb_device *d) tmp16 |= tmp << 8; - dev_dbg(&d->udev->dev, "%s: [%d]IF=%d\n", __func__, i, tmp16); + dev_dbg(&intf->dev, "[%d]IF=%d\n", i, tmp16); addr += 0x10; /* shift for the 2nd tuner params */ } @@ -962,10 +972,24 @@ skip_eeprom: state->af9033_config[i].clock = clock_lut_af9035[tmp]; } + state->no_read = false; + /* Some MXL5007T devices cannot properly handle tuner I2C read ops. */ + if (state->af9033_config[0].tuner == AF9033_TUNER_MXL5007T && + le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA) + + switch (le16_to_cpu(d->udev->descriptor.idProduct)) { + case USB_PID_AVERMEDIA_A867: + case USB_PID_AVERMEDIA_TWINSTAR: + dev_info(&intf->dev, + "Device may have issues with I2C read operations. Enabling fix.\n"); + state->no_read = true; + break; + } + return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -973,10 +997,11 @@ err: static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { + struct usb_interface *intf = d->intf; int ret; u8 val; - dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg); + dev_dbg(&intf->dev, "cmd=%d arg=%d\n", cmd, arg); /* * CEN always enabled by hardware wiring @@ -1010,7 +1035,7 @@ static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d, return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1019,6 +1044,7 @@ err: static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { + struct usb_interface *intf = d->intf; int ret; switch (cmd) { @@ -1076,7 +1102,7 @@ static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1102,9 +1128,10 @@ static int af9035_frontend_callback(void *adapter_priv, int component, { struct i2c_adapter *adap = adapter_priv; struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct usb_interface *intf = d->intf; - dev_dbg(&d->udev->dev, "%s: component=%d cmd=%d arg=%d\n", - __func__, component, cmd, arg); + dev_dbg(&intf->dev, "component=%d cmd=%d arg=%d\n", + component, cmd, arg); switch (component) { case DVB_FRONTEND_COMPONENT_TUNER: @@ -1127,9 +1154,10 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int ret; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); if (!state->af9033_config[adap->id].tuner) { /* unsupported tuner */ @@ -1156,7 +1184,7 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1165,11 +1193,12 @@ static int it930x_frontend_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int ret; struct si2168_config si2168_config; struct i2c_adapter *adapter; - dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); memset(&si2168_config, 0, sizeof(si2168_config)); si2168_config.i2c_adapter = &adapter; @@ -1192,7 +1221,7 @@ static int it930x_frontend_attach(struct dvb_usb_adapter *adap) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1201,9 +1230,10 @@ static int af9035_frontend_detach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int demod2; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); /* * For dual tuner devices we have to resolve 2nd demod client, as there @@ -1279,12 +1309,13 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int ret; struct dvb_frontend *fe; struct i2c_msg msg[1]; u8 tuner_addr; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); /* * XXX: Hack used in that function: we abuse unused I2C address bit [7] @@ -1522,7 +1553,7 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1531,10 +1562,11 @@ static int it930x_tuner_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int ret; struct si2157_config si2157_config; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); /* I2C master bus 2 clock speed 300k */ ret = af9035_wr_reg(d, 0x00f6a7, 0x07); @@ -1590,7 +1622,7 @@ static int it930x_tuner_attach(struct dvb_usb_adapter *adap) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1600,8 +1632,9 @@ static int it930x_tuner_detach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; - dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); if (adap->id == 1) { if (state->i2c_client[3]) @@ -1619,8 +1652,9 @@ static int af9035_tuner_detach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); switch (state->af9033_config[adap->id].tuner) { case AF9033_TUNER_TUA9001: @@ -1646,6 +1680,7 @@ static int af9035_tuner_detach(struct dvb_usb_adapter *adap) static int af9035_init(struct dvb_usb_device *d) { struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret, i; u16 frame_size = (d->udev->speed == USB_SPEED_FULL ? 5 : 87) * 188 / 4; u8 packet_size = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4; @@ -1670,9 +1705,8 @@ static int af9035_init(struct dvb_usb_device *d) { 0x80f9a4, 0x00, 0x01 }, }; - dev_dbg(&d->udev->dev, - "%s: USB speed=%d frame_size=%04x packet_size=%02x\n", - __func__, d->udev->speed, frame_size, packet_size); + dev_dbg(&intf->dev, "USB speed=%d frame_size=%04x packet_size=%02x\n", + d->udev->speed, frame_size, packet_size); /* init endpoints */ for (i = 0; i < ARRAY_SIZE(tab); i++) { @@ -1685,7 +1719,7 @@ static int af9035_init(struct dvb_usb_device *d) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1693,6 +1727,7 @@ err: static int it930x_init(struct dvb_usb_device *d) { struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret, i; u16 frame_size = (d->udev->speed == USB_SPEED_FULL ? 5 : 816) * 188 / 4; u8 packet_size = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4; @@ -1752,9 +1787,8 @@ static int it930x_init(struct dvb_usb_device *d) { 0x00da5a, 0x1f, 0xff }, /* ts_fail_ignore */ }; - dev_dbg(&d->udev->dev, - "%s: USB speed=%d frame_size=%04x packet_size=%02x\n", - __func__, d->udev->speed, frame_size, packet_size); + dev_dbg(&intf->dev, "USB speed=%d frame_size=%04x packet_size=%02x\n", + d->udev->speed, frame_size, packet_size); /* init endpoints */ for (i = 0; i < ARRAY_SIZE(tab); i++) { @@ -1767,7 +1801,7 @@ static int it930x_init(struct dvb_usb_device *d) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1776,6 +1810,7 @@ err: #if IS_ENABLED(CONFIG_RC_CORE) static int af9035_rc_query(struct dvb_usb_device *d) { + struct usb_interface *intf = d->intf; int ret; u32 key; u8 buf[4]; @@ -1801,14 +1836,14 @@ static int af9035_rc_query(struct dvb_usb_device *d) buf[2] << 8 | buf[3]); } - dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 4, buf); + dev_dbg(&intf->dev, "%*ph\n", 4, buf); rc_keydown(d->rc_dev, RC_TYPE_NEC, key, 0); return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1816,6 +1851,7 @@ err: static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) { struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret; u8 tmp; @@ -1823,7 +1859,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: ir_mode=%02x\n", __func__, tmp); + dev_dbg(&intf->dev, "ir_mode=%02x\n", tmp); /* don't activate rc if in HID mode or if not available */ if (tmp == 5) { @@ -1832,7 +1868,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: ir_type=%02x\n", __func__, tmp); + dev_dbg(&intf->dev, "ir_type=%02x\n", tmp); switch (tmp) { case 0: /* NEC */ @@ -1855,7 +1891,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1867,8 +1903,9 @@ static int af9035_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, struct usb_data_stream_properties *stream) { struct dvb_usb_device *d = fe_to_d(fe); + struct usb_interface *intf = d->intf; - dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, fe_to_adap(fe)->id); + dev_dbg(&intf->dev, "adap=%d\n", fe_to_adap(fe)->id); if (d->udev->speed == USB_SPEED_FULL) stream->u.bulk.buffersize = 5 * 188; @@ -1920,7 +1957,7 @@ static int af9035_probe(struct usb_interface *intf, if ((le16_to_cpu(udev->descriptor.idVendor) == USB_VID_TERRATEC) && (le16_to_cpu(udev->descriptor.idProduct) == 0x0099)) { if (!strcmp("Afatech", manufacturer)) { - dev_dbg(&udev->dev, "%s: rejecting device\n", __func__); + dev_dbg(&udev->dev, "rejecting device\n"); return -ENODEV; } } diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index 89e629a24aec..c91d1a3789e6 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -62,6 +62,7 @@ struct state { u8 chip_version; u16 chip_type; u8 dual_mode:1; + u8 no_read:1; u16 eeprom_addr; u8 af9033_i2c_addr[2]; struct af9033_config af9033_config[2]; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index eb7af8cb8aca..6643762a9ff7 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -624,7 +624,7 @@ static int rtl28xxu_identify_state(struct dvb_usb_device *d, const char **name) dev_dbg(&d->intf->dev, "chip_id=%u\n", dev->chip_id); /* Retry failed I2C messages */ - d->i2c_adap.retries = 1; + d->i2c_adap.retries = 3; d->i2c_adap.timeout = msecs_to_jiffies(10); return WARM; diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index a19b5c8b56ff..1a9e1e556706 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -507,9 +507,8 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, if (dev->disconnected) return -ENODEV; - rc = rt_mutex_trylock(&dev->i2c_bus_lock); - if (rc < 0) - return rc; + if (!rt_mutex_trylock(&dev->i2c_bus_lock)) + return -EAGAIN; /* Switch I2C bus if needed */ if (bus != dev->cur_i2c_bus && diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 451e84e962e2..302e284a95eb 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1674,7 +1674,7 @@ static void uvc_delete(struct uvc_device *dev) if (dev->vdev.dev) v4l2_device_unregister(&dev->vdev); #ifdef CONFIG_MEDIA_CONTROLLER - if (media_devnode_is_registered(&dev->mdev.devnode)) + if (media_devnode_is_registered(dev->mdev.devnode)) media_device_unregister(&dev->mdev); media_device_cleanup(&dev->mdev); #endif diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index c04bc6afb965..05eed4be25df 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -142,6 +142,21 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) return interval; } +static __u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format, + const struct uvc_frame *frame) +{ + switch (format->fcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_M420: + return frame->wWidth; + + default: + return format->bpp * frame->wWidth / 8; + } +} + static int uvc_v4l2_try_format(struct uvc_streaming *stream, struct v4l2_format *fmt, struct uvc_streaming_control *probe, struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) @@ -245,7 +260,7 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; @@ -282,7 +297,7 @@ static int uvc_v4l2_get_format(struct uvc_streaming *stream, fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 075a0fe77485..b5589d5f5da4 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1470,6 +1470,7 @@ static unsigned int uvc_endpoint_max_bpi(struct usb_device *dev, switch (dev->speed) { case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); case USB_SPEED_HIGH: psize = usb_endpoint_maxp(&ep->desc); diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 9fbcb67a9ee6..633fc1ab1d7a 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1648,7 +1648,7 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, void *pb, int nonblocking) { unsigned long flags; - int ret; + int ret = 0; /* * Wait for at least one buffer to become available on the done_list. @@ -1664,10 +1664,12 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, spin_lock_irqsave(&q->done_lock, flags); *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry); /* - * Only remove the buffer from done_list if v4l2_buffer can handle all - * the planes. + * Only remove the buffer from done_list if all planes can be + * handled. Some cases such as V4L2 file I/O and DVB have pb + * == NULL; skip the check then as there's nothing to verify. */ - ret = call_bufop(q, verify_planes_array, *vb, pb); + if (pb) + ret = call_bufop(q, verify_planes_array, *vb, pb); if (!ret) list_del(&(*vb)->done_entry); spin_unlock_irqrestore(&q->done_lock, flags); diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 5361197f3e57..e3e47ace7daf 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -753,6 +753,59 @@ void vb2_dma_contig_cleanup_ctx(void *alloc_ctx) } EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx); +/** + * vb2_dma_contig_set_max_seg_size() - configure DMA max segment size + * @dev: device for configuring DMA parameters + * @size: size of DMA max segment size to set + * + * To allow mapping the scatter-list into a single chunk in the DMA + * address space, the device is required to have the DMA max segment + * size parameter set to a value larger than the buffer size. Otherwise, + * the DMA-mapping subsystem will split the mapping into max segment + * size chunks. This function sets the DMA max segment size + * parameter to let DMA-mapping map a buffer as a single chunk in DMA + * address space. + * This code assumes that the DMA-mapping subsystem will merge all + * scatterlist segments if this is really possible (for example when + * an IOMMU is available and enabled). + * Ideally, this parameter should be set by the generic bus code, but it + * is left with the default 64KiB value due to historical litmiations in + * other subsystems (like limited USB host drivers) and there no good + * place to set it to the proper value. + * This function should be called from the drivers, which are known to + * operate on platforms with IOMMU and provide access to shared buffers + * (either USERPTR or DMABUF). This should be done before initializing + * videobuf2 queue. + */ +int vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size) +{ + if (!dev->dma_parms) { + dev->dma_parms = kzalloc(sizeof(dev->dma_parms), GFP_KERNEL); + if (!dev->dma_parms) + return -ENOMEM; + } + if (dma_get_max_seg_size(dev) < size) + return dma_set_max_seg_size(dev, size); + + return 0; +} +EXPORT_SYMBOL_GPL(vb2_dma_contig_set_max_seg_size); + +/* + * vb2_dma_contig_clear_max_seg_size() - release resources for DMA parameters + * @dev: device for configuring DMA parameters + * + * This function releases resources allocated to configure DMA parameters + * (see vb2_dma_contig_set_max_seg_size() function). It should be called from + * device drivers on driver remove. + */ +void vb2_dma_contig_clear_max_seg_size(struct device *dev) +{ + kfree(dev->dma_parms); + dev->dma_parms = NULL; +} +EXPORT_SYMBOL_GPL(vb2_dma_contig_clear_max_seg_size); + MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2"); MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/v4l2-core/videobuf2-v4l2.c b/drivers/media/v4l2-core/videobuf2-v4l2.c index 0b1b8c7b6ce5..7f366f1b0377 100644 --- a/drivers/media/v4l2-core/videobuf2-v4l2.c +++ b/drivers/media/v4l2-core/videobuf2-v4l2.c @@ -74,6 +74,11 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer return 0; } +static int __verify_planes_array_core(struct vb2_buffer *vb, const void *pb) +{ + return __verify_planes_array(vb, pb); +} + /** * __verify_length() - Verify that the bytesused value for each plane fits in * the plane length and that the data offset doesn't exceed the bytesused value. @@ -437,6 +442,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, } static const struct vb2_buf_ops v4l2_buf_ops = { + .verify_planes_array = __verify_planes_array_core, .fill_user_buffer = __fill_v4l2_buffer, .fill_vb2_buffer = __fill_vb2_buffer, .copy_timestamp = __copy_timestamp, diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 216648233874..06af99f64ad8 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -21,6 +21,7 @@ #include <linux/sizes.h> #include <linux/of_reserved_mem.h> #include <linux/sort.h> +#include <linux/slab.h> #define MAX_RESERVED_REGIONS 16 static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; @@ -296,53 +297,95 @@ static inline struct reserved_mem *__find_rmem(struct device_node *node) return NULL; } +struct rmem_assigned_device { + struct device *dev; + struct reserved_mem *rmem; + struct list_head list; +}; + +static LIST_HEAD(of_rmem_assigned_device_list); +static DEFINE_MUTEX(of_rmem_assigned_device_mutex); + /** - * of_reserved_mem_device_init() - assign reserved memory region to given device + * of_reserved_mem_device_init_by_idx() - assign reserved memory region to + * given device + * @dev: Pointer to the device to configure + * @np: Pointer to the device_node with 'reserved-memory' property + * @idx: Index of selected region * - * This function assign memory region pointed by "memory-region" device tree - * property to the given device. + * This function assigns respective DMA-mapping operations based on reserved + * memory region specified by 'memory-region' property in @np node to the @dev + * device. When driver needs to use more than one reserved memory region, it + * should allocate child devices and initialize regions by name for each of + * child device. + * + * Returns error code or zero on success. */ -int of_reserved_mem_device_init(struct device *dev) +int of_reserved_mem_device_init_by_idx(struct device *dev, + struct device_node *np, int idx) { + struct rmem_assigned_device *rd; + struct device_node *target; struct reserved_mem *rmem; - struct device_node *np; int ret; - np = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!np) + if (!np || !dev) + return -EINVAL; + + target = of_parse_phandle(np, "memory-region", idx); + if (!target) return -ENODEV; - rmem = __find_rmem(np); - of_node_put(np); + rmem = __find_rmem(target); + of_node_put(target); if (!rmem || !rmem->ops || !rmem->ops->device_init) return -EINVAL; + rd = kmalloc(sizeof(struct rmem_assigned_device), GFP_KERNEL); + if (!rd) + return -ENOMEM; + ret = rmem->ops->device_init(rmem, dev); - if (ret == 0) + if (ret == 0) { + rd->dev = dev; + rd->rmem = rmem; + + mutex_lock(&of_rmem_assigned_device_mutex); + list_add(&rd->list, &of_rmem_assigned_device_list); + mutex_unlock(&of_rmem_assigned_device_mutex); + dev_info(dev, "assigned reserved memory node %s\n", rmem->name); + } else { + kfree(rd); + } return ret; } -EXPORT_SYMBOL_GPL(of_reserved_mem_device_init); +EXPORT_SYMBOL_GPL(of_reserved_mem_device_init_by_idx); /** * of_reserved_mem_device_release() - release reserved memory device structures + * @dev: Pointer to the device to deconfigure * * This function releases structures allocated for memory region handling for * the given device. */ void of_reserved_mem_device_release(struct device *dev) { - struct reserved_mem *rmem; - struct device_node *np; - - np = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!np) - return; - - rmem = __find_rmem(np); - of_node_put(np); + struct rmem_assigned_device *rd; + struct reserved_mem *rmem = NULL; + + mutex_lock(&of_rmem_assigned_device_mutex); + list_for_each_entry(rd, &of_rmem_assigned_device_list, list) { + if (rd->dev == dev) { + rmem = rd->rmem; + list_del(&rd->list); + kfree(rd); + break; + } + } + mutex_unlock(&of_rmem_assigned_device_mutex); if (!rmem || !rmem->ops || !rmem->ops->device_release) return; diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index de7e9f52e7eb..ee9186843371 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -25,18 +25,8 @@ source "drivers/staging/media/cxd2099/Kconfig" source "drivers/staging/media/davinci_vpfe/Kconfig" -source "drivers/staging/media/mn88472/Kconfig" - -source "drivers/staging/media/mx2/Kconfig" - -source "drivers/staging/media/mx3/Kconfig" - -source "drivers/staging/media/omap1/Kconfig" - source "drivers/staging/media/omap4iss/Kconfig" -source "drivers/staging/media/timb/Kconfig" - source "drivers/staging/media/tw686x-kh/Kconfig" # Keep LIRC at the end, as it has sub-menus diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 60a35b3a47e7..8c05d0ace0fb 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -2,10 +2,5 @@ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ -obj-$(CONFIG_VIDEO_MX2) += mx2/ -obj-$(CONFIG_VIDEO_MX3) += mx3/ -obj-$(CONFIG_VIDEO_OMAP1) += omap1/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ -obj-$(CONFIG_DVB_MN88472) += mn88472/ -obj-$(CONFIG_VIDEO_TIMBERDALE) += timb/ obj-$(CONFIG_VIDEO_TW686X_KH) += tw686x-kh/ diff --git a/drivers/staging/media/mn88472/Kconfig b/drivers/staging/media/mn88472/Kconfig deleted file mode 100644 index a85c90a60bce..000000000000 --- a/drivers/staging/media/mn88472/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -config DVB_MN88472 - tristate "Panasonic MN88472" - depends on DVB_CORE && I2C - select REGMAP_I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - Say Y when you want to support this frontend. diff --git a/drivers/staging/media/mn88472/Makefile b/drivers/staging/media/mn88472/Makefile deleted file mode 100644 index 5987b7e6d82a..000000000000 --- a/drivers/staging/media/mn88472/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-$(CONFIG_DVB_MN88472) += mn88472.o - -ccflags-y += -Idrivers/media/dvb-core/ -ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/tuners/ diff --git a/drivers/staging/media/mn88472/TODO b/drivers/staging/media/mn88472/TODO deleted file mode 100644 index b90a14be3beb..000000000000 --- a/drivers/staging/media/mn88472/TODO +++ /dev/null @@ -1,21 +0,0 @@ -Driver general quality is not good enough for mainline. Also, other -device drivers (USB-bridge, tuner) needed for Astrometa receiver in -question could need some changes. However, if that driver is mainlined -due to some other device than Astrometa, unrelated TODOs could be -skipped. In that case rtl28xxu driver needs module parameter to prevent -driver loading. - -Required TODOs: -* missing lock flags -* I2C errors -* tuner sensitivity - -*Do not* send any patch fixing checkpatch.pl issues. Currently it passes -checkpatch.pl tests. I don't want waste my time to review this kind of -trivial stuff. *Do not* add missing register I/O error checks. Those are -missing for the reason it is much easier to compare I2C data sniffs when -there is less lines. Those error checks are about the last thing to be added. - -Patches should be submitted to: -linux-media@vger.kernel.org and Antti Palosaari <crope@iki.fi> - diff --git a/drivers/staging/media/mx2/Kconfig b/drivers/staging/media/mx2/Kconfig deleted file mode 100644 index beaa885cf104..000000000000 --- a/drivers/staging/media/mx2/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config VIDEO_MX2 - tristate "i.MX27 Camera Sensor Interface driver" - depends on VIDEO_DEV && SOC_CAMERA - depends on SOC_IMX27 || COMPILE_TEST - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - ---help--- - This is a v4l2 driver for the i.MX27 Camera Sensor Interface - - This driver is deprecated: it should become a stand-alone driver - instead of using the soc-camera framework. - - Unless someone is willing to take this on (unlikely with such - ancient hardware) it is going to be removed from the kernel - soon. diff --git a/drivers/staging/media/mx2/Makefile b/drivers/staging/media/mx2/Makefile deleted file mode 100644 index fc5b2826a558..000000000000 --- a/drivers/staging/media/mx2/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# Makefile for i.MX27 Camera Sensor driver - -obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o diff --git a/drivers/staging/media/mx2/TODO b/drivers/staging/media/mx2/TODO deleted file mode 100644 index bc68fa443a3e..000000000000 --- a/drivers/staging/media/mx2/TODO +++ /dev/null @@ -1,10 +0,0 @@ -This driver is deprecated: it should become a stand-alone driver instead of -using the soc-camera framework. - -Unless someone is willing to take this on (unlikely with such ancient -hardware) it is going to be removed from the kernel soon. - -Note that trivial patches will not be accepted anymore, only a full conversion. - -If you want to convert this driver, please contact the linux-media mailinglist -(see http://linuxtv.org/lists.php). diff --git a/drivers/staging/media/mx2/mx2_camera.c b/drivers/staging/media/mx2/mx2_camera.c deleted file mode 100644 index 48dd5b7851b5..000000000000 --- a/drivers/staging/media/mx2/mx2_camera.c +++ /dev/null @@ -1,1636 +0,0 @@ -/* - * V4L2 Driver for i.MX27 camera host - * - * Copyright (C) 2008, Sascha Hauer, Pengutronix - * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography - * Copyright (C) 2012, Javier Martin, Vista Silicon S.L. - * - * 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. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/dma-mapping.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/gcd.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/math64.h> -#include <linux/mm.h> -#include <linux/moduleparam.h> -#include <linux/time.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/clk.h> - -#include <media/v4l2-common.h> -#include <media/v4l2-dev.h> -#include <media/videobuf2-v4l2.h> -#include <media/videobuf2-dma-contig.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> - -#include <linux/videodev2.h> - -#include <linux/platform_data/media/camera-mx2.h> - -#include <asm/dma.h> - -#define MX2_CAM_DRV_NAME "mx2-camera" -#define MX2_CAM_VERSION "0.0.6" -#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera" - -/* reset values */ -#define CSICR1_RESET_VAL 0x40000800 -#define CSICR2_RESET_VAL 0x0 -#define CSICR3_RESET_VAL 0x0 - -/* csi control reg 1 */ -#define CSICR1_SWAP16_EN (1 << 31) -#define CSICR1_EXT_VSYNC (1 << 30) -#define CSICR1_EOF_INTEN (1 << 29) -#define CSICR1_PRP_IF_EN (1 << 28) -#define CSICR1_CCIR_MODE (1 << 27) -#define CSICR1_COF_INTEN (1 << 26) -#define CSICR1_SF_OR_INTEN (1 << 25) -#define CSICR1_RF_OR_INTEN (1 << 24) -#define CSICR1_STATFF_LEVEL (3 << 22) -#define CSICR1_STATFF_INTEN (1 << 21) -#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) -#define CSICR1_RXFF_INTEN (1 << 18) -#define CSICR1_SOF_POL (1 << 17) -#define CSICR1_SOF_INTEN (1 << 16) -#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12) -#define CSICR1_HSYNC_POL (1 << 11) -#define CSICR1_CCIR_EN (1 << 10) -#define CSICR1_MCLKEN (1 << 9) -#define CSICR1_FCC (1 << 8) -#define CSICR1_PACK_DIR (1 << 7) -#define CSICR1_CLR_STATFIFO (1 << 6) -#define CSICR1_CLR_RXFIFO (1 << 5) -#define CSICR1_GCLK_MODE (1 << 4) -#define CSICR1_INV_DATA (1 << 3) -#define CSICR1_INV_PCLK (1 << 2) -#define CSICR1_REDGE (1 << 1) -#define CSICR1_FMT_MASK (CSICR1_PACK_DIR | CSICR1_SWAP16_EN) - -#define SHIFT_STATFF_LEVEL 22 -#define SHIFT_RXFF_LEVEL 19 -#define SHIFT_MCLKDIV 12 - -#define SHIFT_FRMCNT 16 - -#define CSICR1 0x00 -#define CSICR2 0x04 -#define CSISR 0x08 -#define CSISTATFIFO 0x0c -#define CSIRFIFO 0x10 -#define CSIRXCNT 0x14 -#define CSICR3 0x1c -#define CSIDMASA_STATFIFO 0x20 -#define CSIDMATA_STATFIFO 0x24 -#define CSIDMASA_FB1 0x28 -#define CSIDMASA_FB2 0x2c -#define CSIFBUF_PARA 0x30 -#define CSIIMAG_PARA 0x34 - -/* EMMA PrP */ -#define PRP_CNTL 0x00 -#define PRP_INTR_CNTL 0x04 -#define PRP_INTRSTATUS 0x08 -#define PRP_SOURCE_Y_PTR 0x0c -#define PRP_SOURCE_CB_PTR 0x10 -#define PRP_SOURCE_CR_PTR 0x14 -#define PRP_DEST_RGB1_PTR 0x18 -#define PRP_DEST_RGB2_PTR 0x1c -#define PRP_DEST_Y_PTR 0x20 -#define PRP_DEST_CB_PTR 0x24 -#define PRP_DEST_CR_PTR 0x28 -#define PRP_SRC_FRAME_SIZE 0x2c -#define PRP_DEST_CH1_LINE_STRIDE 0x30 -#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34 -#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38 -#define PRP_CH1_OUT_IMAGE_SIZE 0x3c -#define PRP_CH2_OUT_IMAGE_SIZE 0x40 -#define PRP_SRC_LINE_STRIDE 0x44 -#define PRP_CSC_COEF_012 0x48 -#define PRP_CSC_COEF_345 0x4c -#define PRP_CSC_COEF_678 0x50 -#define PRP_CH1_RZ_HORI_COEF1 0x54 -#define PRP_CH1_RZ_HORI_COEF2 0x58 -#define PRP_CH1_RZ_HORI_VALID 0x5c -#define PRP_CH1_RZ_VERT_COEF1 0x60 -#define PRP_CH1_RZ_VERT_COEF2 0x64 -#define PRP_CH1_RZ_VERT_VALID 0x68 -#define PRP_CH2_RZ_HORI_COEF1 0x6c -#define PRP_CH2_RZ_HORI_COEF2 0x70 -#define PRP_CH2_RZ_HORI_VALID 0x74 -#define PRP_CH2_RZ_VERT_COEF1 0x78 -#define PRP_CH2_RZ_VERT_COEF2 0x7c -#define PRP_CH2_RZ_VERT_VALID 0x80 - -#define PRP_CNTL_CH1EN (1 << 0) -#define PRP_CNTL_CH2EN (1 << 1) -#define PRP_CNTL_CSIEN (1 << 2) -#define PRP_CNTL_DATA_IN_YUV420 (0 << 3) -#define PRP_CNTL_DATA_IN_YUV422 (1 << 3) -#define PRP_CNTL_DATA_IN_RGB16 (2 << 3) -#define PRP_CNTL_DATA_IN_RGB32 (3 << 3) -#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5) -#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5) -#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5) -#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5) -#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7) -#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7) -#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7) -#define PRP_CNTL_CH1_LEN (1 << 9) -#define PRP_CNTL_CH2_LEN (1 << 10) -#define PRP_CNTL_SKIP_FRAME (1 << 11) -#define PRP_CNTL_SWRST (1 << 12) -#define PRP_CNTL_CLKEN (1 << 13) -#define PRP_CNTL_WEN (1 << 14) -#define PRP_CNTL_CH1BYP (1 << 15) -#define PRP_CNTL_IN_TSKIP(x) ((x) << 16) -#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19) -#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22) -#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25) -#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27) -#define PRP_CNTL_CH2B1EN (1 << 29) -#define PRP_CNTL_CH2B2EN (1 << 30) -#define PRP_CNTL_CH2FEN (1 << 31) - -/* IRQ Enable and status register */ -#define PRP_INTR_RDERR (1 << 0) -#define PRP_INTR_CH1WERR (1 << 1) -#define PRP_INTR_CH2WERR (1 << 2) -#define PRP_INTR_CH1FC (1 << 3) -#define PRP_INTR_CH2FC (1 << 5) -#define PRP_INTR_LBOVF (1 << 7) -#define PRP_INTR_CH2OVF (1 << 8) - -/* Resizing registers */ -#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24) -#define PRP_RZ_VALID_BILINEAR (1 << 31) - -#define MAX_VIDEO_MEM 16 - -#define RESIZE_NUM_MIN 1 -#define RESIZE_NUM_MAX 20 -#define BC_COEF 3 -#define SZ_COEF (1 << BC_COEF) - -#define RESIZE_DIR_H 0 -#define RESIZE_DIR_V 1 - -#define RESIZE_ALGO_BILINEAR 0 -#define RESIZE_ALGO_AVERAGING 1 - -struct mx2_prp_cfg { - int channel; - u32 in_fmt; - u32 out_fmt; - u32 src_pixel; - u32 ch1_pixel; - u32 irq_flags; - u32 csicr1; -}; - -/* prp resizing parameters */ -struct emma_prp_resize { - int algo; /* type of algorithm used */ - int len; /* number of coefficients */ - unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */ -}; - -/* prp configuration for a client-host fmt pair */ -struct mx2_fmt_cfg { - u32 in_fmt; - u32 out_fmt; - struct mx2_prp_cfg cfg; -}; - -struct mx2_buf_internal { - struct list_head queue; - int bufnum; - bool discard; -}; - -/* buffer for one video frame */ -struct mx2_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct mx2_buf_internal internal; -}; - -enum mx2_camera_type { - IMX27_CAMERA, -}; - -struct mx2_camera_dev { - struct device *dev; - struct soc_camera_host soc_host; - struct clk *clk_emma_ahb, *clk_emma_ipg; - struct clk *clk_csi_ahb, *clk_csi_per; - - void __iomem *base_csi, *base_emma; - - struct mx2_camera_platform_data *pdata; - unsigned long platform_flags; - - struct list_head capture; - struct list_head active_bufs; - struct list_head discard; - - spinlock_t lock; - - int dma; - struct mx2_buffer *active; - struct mx2_buffer *fb1_active; - struct mx2_buffer *fb2_active; - - u32 csicr1; - enum mx2_camera_type devtype; - - struct mx2_buf_internal buf_discard[2]; - void *discard_buffer; - dma_addr_t discard_buffer_dma; - size_t discard_size; - struct mx2_fmt_cfg *emma_prp; - struct emma_prp_resize resizing[2]; - unsigned int s_width, s_height; - u32 frame_count; - struct vb2_alloc_ctx *alloc_ctx; -}; - -static struct platform_device_id mx2_camera_devtype[] = { - { - .name = "imx27-camera", - .driver_data = IMX27_CAMERA, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, mx2_camera_devtype); - -static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf) -{ - return container_of(int_buf, struct mx2_buffer, internal); -} - -static struct mx2_fmt_cfg mx27_emma_prp_table[] = { - /* - * This is a generic configuration which is valid for most - * prp input-output format combinations. - * We set the incoming and outgoing pixelformat to a - * 16 Bit wide format and adjust the bytesperline - * accordingly. With this configuration the inputdata - * will not be changed by the emma and could be any type - * of 16 Bit Pixelformat. - */ - { - .in_fmt = 0, - .out_fmt = 0, - .cfg = { - .channel = 1, - .in_fmt = PRP_CNTL_DATA_IN_RGB16, - .out_fmt = PRP_CNTL_CH1_OUT_RGB16, - .src_pixel = 0x2ca00565, /* RGB565 */ - .ch1_pixel = 0x2ca00565, /* RGB565 */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR | - PRP_INTR_CH1FC | PRP_INTR_LBOVF, - .csicr1 = 0, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8, - .out_fmt = V4L2_PIX_FMT_YUYV, - .cfg = { - .channel = 1, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH1_OUT_YUV422, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR | - PRP_INTR_CH1FC | PRP_INTR_LBOVF, - .csicr1 = CSICR1_SWAP16_EN, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8, - .out_fmt = V4L2_PIX_FMT_YUYV, - .cfg = { - .channel = 1, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH1_OUT_YUV422, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR | - PRP_INTR_CH1FC | PRP_INTR_LBOVF, - .csicr1 = CSICR1_PACK_DIR, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8, - .out_fmt = V4L2_PIX_FMT_YUV420, - .cfg = { - .channel = 2, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH2_OUT_YUV420, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR | - PRP_INTR_CH2FC | PRP_INTR_LBOVF | - PRP_INTR_CH2OVF, - .csicr1 = CSICR1_PACK_DIR, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8, - .out_fmt = V4L2_PIX_FMT_YUV420, - .cfg = { - .channel = 2, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH2_OUT_YUV420, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR | - PRP_INTR_CH2FC | PRP_INTR_LBOVF | - PRP_INTR_CH2OVF, - .csicr1 = CSICR1_SWAP16_EN, - } - }, -}; - -static struct mx2_fmt_cfg *mx27_emma_prp_get_format(u32 in_fmt, u32 out_fmt) -{ - int i; - - for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++) - if ((mx27_emma_prp_table[i].in_fmt == in_fmt) && - (mx27_emma_prp_table[i].out_fmt == out_fmt)) { - return &mx27_emma_prp_table[i]; - } - /* If no match return the most generic configuration */ - return &mx27_emma_prp_table[0]; -}; - -static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, - unsigned long phys, int bufnum) -{ - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - - if (prp->cfg.channel == 1) { - writel(phys, pcdev->base_emma + - PRP_DEST_RGB1_PTR + 4 * bufnum); - } else { - writel(phys, pcdev->base_emma + - PRP_DEST_Y_PTR - 0x14 * bufnum); - if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { - u32 imgsize = pcdev->soc_host.icd->user_height * - pcdev->soc_host.icd->user_width; - - writel(phys + imgsize, pcdev->base_emma + - PRP_DEST_CB_PTR - 0x14 * bufnum); - writel(phys + ((5 * imgsize) / 4), pcdev->base_emma + - PRP_DEST_CR_PTR - 0x14 * bufnum); - } - } -} - -static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) -{ - clk_disable_unprepare(pcdev->clk_csi_ahb); - clk_disable_unprepare(pcdev->clk_csi_per); - writel(0, pcdev->base_csi + CSICR1); - writel(0, pcdev->base_emma + PRP_CNTL); -} - -static int mx2_camera_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void mx2_camera_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "Camera driver detached from camera %d\n", - icd->devnum); -} - -/* - * The following two functions absolutely depend on the fact, that - * there can be only one camera on mx2 camera sensor interface - */ -static int mx2_camera_clock_start(struct soc_camera_host *ici) -{ - struct mx2_camera_dev *pcdev = ici->priv; - int ret; - u32 csicr1; - - ret = clk_prepare_enable(pcdev->clk_csi_ahb); - if (ret < 0) - return ret; - - ret = clk_prepare_enable(pcdev->clk_csi_per); - if (ret < 0) - goto exit_csi_ahb; - - csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC | - CSICR1_RXFF_LEVEL(0); - - pcdev->csicr1 = csicr1; - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - - pcdev->frame_count = 0; - - return 0; - -exit_csi_ahb: - clk_disable_unprepare(pcdev->clk_csi_ahb); - - return ret; -} - -static void mx2_camera_clock_stop(struct soc_camera_host *ici) -{ - struct mx2_camera_dev *pcdev = ici->priv; - - mx2_camera_deactivate(pcdev); -} - -/* - * Videobuf operations - */ -static int mx2_videobuf_setup(struct vb2_queue *vq, - unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - - dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]); - - alloc_ctxs[0] = pcdev->alloc_ctx; - - sizes[0] = icd->sizeimage; - - if (0 == *count) - *count = 32; - if (!*num_planes && - sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0]; - - *num_planes = 1; - - return 0; -} - -static int mx2_videobuf_prepare(struct vb2_buffer *vb) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - int ret = 0; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - -#ifdef DEBUG - /* - * This can be useful if you want to see if we actually fill - * the buffer with something - */ - memset((void *)vb2_plane_vaddr(vb, 0), - 0xaa, vb2_get_plane_payload(vb, 0)); -#endif - - vb2_set_plane_payload(vb, 0, icd->sizeimage); - if (vb2_plane_vaddr(vb, 0) && - vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { - ret = -EINVAL; - goto out; - } - - return 0; - -out: - return ret; -} - -static void mx2_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_buffer *buf = container_of(vbuf, struct mx2_buffer, vb); - unsigned long flags; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - spin_lock_irqsave(&pcdev->lock, flags); - - list_add_tail(&buf->internal.queue, &pcdev->capture); - - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, - int bytesperline) -{ - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - - writel((pcdev->s_width << 16) | pcdev->s_height, - pcdev->base_emma + PRP_SRC_FRAME_SIZE); - writel(prp->cfg.src_pixel, - pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); - if (prp->cfg.channel == 1) { - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); - writel(bytesperline, - pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); - writel(prp->cfg.ch1_pixel, - pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); - } else { /* channel 2 */ - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); - } - - /* Enable interrupts */ - writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); -} - -static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev) -{ - int dir; - - for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { - unsigned char *s = pcdev->resizing[dir].s; - int len = pcdev->resizing[dir].len; - unsigned int coeff[2] = {0, 0}; - unsigned int valid = 0; - int i; - - if (len == 0) - continue; - - for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) { - int j; - - j = i > 9 ? 1 : 0; - coeff[j] = (coeff[j] << BC_COEF) | - (s[i] & (SZ_COEF - 1)); - - if (i == 5 || i == 15) - coeff[j] <<= 1; - - valid = (valid << 1) | (s[i] >> BC_COEF); - } - - valid |= PRP_RZ_VALID_TBL_LEN(len); - - if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR) - valid |= PRP_RZ_VALID_BILINEAR; - - if (pcdev->emma_prp->cfg.channel == 1) { - if (dir == RESIZE_DIR_H) { - writel(coeff[0], pcdev->base_emma + - PRP_CH1_RZ_HORI_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH1_RZ_HORI_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH1_RZ_HORI_VALID); - } else { - writel(coeff[0], pcdev->base_emma + - PRP_CH1_RZ_VERT_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH1_RZ_VERT_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH1_RZ_VERT_VALID); - } - } else { - if (dir == RESIZE_DIR_H) { - writel(coeff[0], pcdev->base_emma + - PRP_CH2_RZ_HORI_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH2_RZ_HORI_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH2_RZ_HORI_VALID); - } else { - writel(coeff[0], pcdev->base_emma + - PRP_CH2_RZ_VERT_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH2_RZ_VERT_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH2_RZ_VERT_VALID); - } - } - } -} - -static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - struct vb2_buffer *vb; - struct mx2_buffer *buf; - unsigned long phys; - int bytesperline; - unsigned long flags; - - if (count < 2) - return -ENOBUFS; - - spin_lock_irqsave(&pcdev->lock, flags); - - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 0; - vb = &buf->vb.vb2_buf; - - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 1; - vb = &buf->vb.vb2_buf; - - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - bytesperline = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytesperline < 0) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return bytesperline; - } - - /* - * I didn't manage to properly enable/disable the prp - * on a per frame basis during running transfers, - * thus we allocate a buffer here and use it to - * discard frames when no buffer is available. - * Feel free to work on this ;) - */ - pcdev->discard_size = icd->user_height * bytesperline; - pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, - &pcdev->discard_buffer_dma, GFP_ATOMIC); - if (!pcdev->discard_buffer) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return -ENOMEM; - } - - pcdev->buf_discard[0].discard = true; - list_add_tail(&pcdev->buf_discard[0].queue, - &pcdev->discard); - - pcdev->buf_discard[1].discard = true; - list_add_tail(&pcdev->buf_discard[1].queue, - &pcdev->discard); - - mx2_prp_resize_commit(pcdev); - - mx27_camera_emma_buf_init(icd, bytesperline); - - if (prp->cfg.channel == 1) { - writel(PRP_CNTL_CH1EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH1_LEN | - PRP_CNTL_CH1BYP | - PRP_CNTL_CH1_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } else { - writel(PRP_CNTL_CH2EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH2_LEN | - PRP_CNTL_CH2_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } - spin_unlock_irqrestore(&pcdev->lock, flags); - - return 0; -} - -static void mx2_stop_streaming(struct vb2_queue *q) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - unsigned long flags; - void *b; - u32 cntl; - - spin_lock_irqsave(&pcdev->lock, flags); - - cntl = readl(pcdev->base_emma + PRP_CNTL); - if (prp->cfg.channel == 1) { - writel(cntl & ~PRP_CNTL_CH1EN, - pcdev->base_emma + PRP_CNTL); - } else { - writel(cntl & ~PRP_CNTL_CH2EN, - pcdev->base_emma + PRP_CNTL); - } - INIT_LIST_HEAD(&pcdev->capture); - INIT_LIST_HEAD(&pcdev->active_bufs); - INIT_LIST_HEAD(&pcdev->discard); - - b = pcdev->discard_buffer; - pcdev->discard_buffer = NULL; - - spin_unlock_irqrestore(&pcdev->lock, flags); - - dma_free_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, b, pcdev->discard_buffer_dma); -} - -static struct vb2_ops mx2_videobuf_ops = { - .queue_setup = mx2_videobuf_setup, - .buf_prepare = mx2_videobuf_prepare, - .buf_queue = mx2_videobuf_queue, - .start_streaming = mx2_start_streaming, - .stop_streaming = mx2_stop_streaming, -}; - -static int mx2_camera_init_videobuf(struct vb2_queue *q, - struct soc_camera_device *icd) -{ - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = icd; - q->ops = &mx2_videobuf_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct mx2_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - return vb2_queue_init(q); -} - -#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | \ - V4L2_MBUS_PCLK_SAMPLE_FALLING | \ - V4L2_MBUS_DATA_ACTIVE_HIGH | \ - V4L2_MBUS_DATA_ACTIVE_LOW) - -static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) -{ - int count = 0; - - readl(pcdev->base_emma + PRP_CNTL); - writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); - while (count++ < 100) { - if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) - return 0; - barrier(); - udelay(1); - } - - return -ETIMEDOUT; -} - -static int mx2_camera_set_bus_param(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long common_flags; - int ret; - int bytesperline; - u32 csicr1 = pcdev->csicr1; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%x\n", - cfg.flags, MX2_BUS_FLAGS); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = MX2_BUS_FLAGS; - } - - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - } - - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); - return ret; - } - - csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1; - - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - csicr1 |= CSICR1_REDGE; - if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) - csicr1 |= CSICR1_SOF_POL; - if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) - csicr1 |= CSICR1_HSYNC_POL; - if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC) - csicr1 |= CSICR1_EXT_VSYNC; - if (pcdev->platform_flags & MX2_CAMERA_CCIR) - csicr1 |= CSICR1_CCIR_EN; - if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE) - csicr1 |= CSICR1_CCIR_MODE; - if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK) - csicr1 |= CSICR1_GCLK_MODE; - if (pcdev->platform_flags & MX2_CAMERA_INV_DATA) - csicr1 |= CSICR1_INV_DATA; - - pcdev->csicr1 = csicr1; - - bytesperline = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytesperline < 0) - return bytesperline; - - ret = mx27_camera_emma_prp_reset(pcdev); - if (ret) - return ret; - - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - - return 0; -} - -static int mx2_camera_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) -{ - struct v4l2_crop a_writable = *a; - struct v4l2_rect *rect = &a_writable.c; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); - soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096); - - ret = v4l2_subdev_call(sd, video, s_crop, a); - if (ret < 0) - return ret; - - /* The capture device might have changed its output */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - dev_dbg(icd->parent, "Sensor cropped %dx%d\n", - mf->width, mf->height); - - icd->user_width = mf->width; - icd->user_height = mf->height; - - return ret; -} - -static int mx2_camera_get_formats(struct soc_camera_device *icd, - unsigned int idx, - struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_mbus_pixelfmt *fmt; - struct device *dev = icd->parent; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - int ret, formats = 0; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* no more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code); - return 0; - } - - if (code.code == MEDIA_BUS_FMT_YUYV8_2X8 || - code.code == MEDIA_BUS_FMT_UYVY8_2X8) { - formats++; - if (xlate) { - /* - * CH2 can output YUV420 which is a standard format in - * soc_mediabus.c - */ - xlate->host_fmt = - soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_1_5X8); - xlate->code = code.code; - dev_dbg(dev, "Providing host format %s for sensor code %d\n", - xlate->host_fmt->name, code.code); - xlate++; - } - } - - if (code.code == MEDIA_BUS_FMT_UYVY8_2X8) { - formats++; - if (xlate) { - xlate->host_fmt = - soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_2X8); - xlate->code = code.code; - dev_dbg(dev, "Providing host format %s for sensor code %d\n", - xlate->host_fmt->name, code.code); - xlate++; - } - } - - /* Generic pass-trough */ - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - xlate++; - } - return formats; -} - -static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, - struct v4l2_mbus_framefmt *mf_in, - struct v4l2_pix_format *pix_out, bool apply) -{ - unsigned int num, den; - unsigned long m; - int i, dir; - - for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { - struct emma_prp_resize tmprsz; - unsigned char *s = tmprsz.s; - int len = 0; - int in, out; - - if (dir == RESIZE_DIR_H) { - in = mf_in->width; - out = pix_out->width; - } else { - in = mf_in->height; - out = pix_out->height; - } - - if (in < out) - return -EINVAL; - else if (in == out) - continue; - - /* Calculate ratio */ - m = gcd(in, out); - num = in / m; - den = out / m; - if (num > RESIZE_NUM_MAX) - return -EINVAL; - - if ((num >= 2 * den) && (den == 1) && - (num < 9) && (!(num & 0x01))) { - int sum = 0; - int j; - - /* Average scaling for >= 2:1 ratios */ - /* Support can be added for num >=9 and odd values */ - - tmprsz.algo = RESIZE_ALGO_AVERAGING; - len = num; - - for (i = 0; i < (len / 2); i++) - s[i] = 8; - - do { - for (i = 0; i < (len / 2); i++) { - s[i] = s[i] >> 1; - sum = 0; - for (j = 0; j < (len / 2); j++) - sum += s[j]; - if (sum == 4) - break; - } - } while (sum != 4); - - for (i = (len / 2); i < len; i++) - s[i] = s[len - i - 1]; - - s[len - 1] |= SZ_COEF; - } else { - /* bilinear scaling for < 2:1 ratios */ - int v; /* overflow counter */ - int coeff, nxt; /* table output */ - int in_pos_inc = 2 * den; - int out_pos = num; - int out_pos_inc = 2 * num; - int init_carry = num - den; - int carry = init_carry; - - tmprsz.algo = RESIZE_ALGO_BILINEAR; - v = den + in_pos_inc; - do { - coeff = v - out_pos; - out_pos += out_pos_inc; - carry += out_pos_inc; - for (nxt = 0; v < out_pos; nxt++) { - v += in_pos_inc; - carry -= in_pos_inc; - } - - if (len > RESIZE_NUM_MAX) - return -EINVAL; - - coeff = ((coeff << BC_COEF) + - (in_pos_inc >> 1)) / in_pos_inc; - - if (coeff >= (SZ_COEF - 1)) - coeff--; - - coeff |= SZ_COEF; - s[len] = (unsigned char)coeff; - len++; - - for (i = 1; i < nxt; i++) { - if (len >= RESIZE_NUM_MAX) - return -EINVAL; - s[len] = 0; - len++; - } - } while (carry != init_carry); - } - tmprsz.len = len; - if (dir == RESIZE_DIR_H) - mf_in->width = pix_out->width; - else - mf_in->height = pix_out->height; - - if (apply) - memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz)); - } - return 0; -} - -static int mx2_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - - dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", - pix->pixelformat); - return -EINVAL; - } - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - - /* Store width and height returned by the sensor for resizing */ - pcdev->s_width = mf->width; - pcdev->s_height = mf->height; - dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", - __func__, pcdev->s_width, pcdev->s_height); - - pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); - - memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); - if ((mf->width != pix->width || mf->height != pix->height) && - pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { - if (mx2_emmaprp_resize(pcdev, mf, pix, true) < 0) - dev_dbg(icd->parent, "%s: can't resize\n", __func__); - } - - if (mf->code != xlate->code) - return -EINVAL; - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; - - dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - return 0; -} - -static int mx2_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *emma_prp; - int ret; - - dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (pixfmt && !xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - /* - * limit to MX27 hardware capabilities: width must be a multiple of 8 as - * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual). - */ - pix->width &= ~0x7; - - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", - __func__, pcdev->s_width, pcdev->s_height); - - /* If the sensor does not support image size try PrP resizing */ - emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); - - if ((mf->width != pix->width || mf->height != pix->height) && - emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { - if (mx2_emmaprp_resize(pcdev, mf, pix, false) < 0) - dev_dbg(icd->parent, "%s: can't resize\n", __func__); - } - - if (mf->field == V4L2_FIELD_ANY) - mf->field = V4L2_FIELD_NONE; - /* - * Driver supports interlaced images provided they have - * both fields so that they can be processed as if they - * were progressive. - */ - if (mf->field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf->field)) { - dev_err(icd->parent, "Field type %d unsupported.\n", - mf->field); - return -EINVAL; - } - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - - dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - return 0; -} - -static int mx2_camera_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the friendly caller:-> */ - strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static unsigned int mx2_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_poll(&icd->vb2_vidq, file, pt); -} - -static struct soc_camera_host_ops mx2_soc_camera_host_ops = { - .owner = THIS_MODULE, - .add = mx2_camera_add_device, - .remove = mx2_camera_remove_device, - .clock_start = mx2_camera_clock_start, - .clock_stop = mx2_camera_clock_stop, - .set_fmt = mx2_camera_set_fmt, - .set_crop = mx2_camera_set_crop, - .get_formats = mx2_camera_get_formats, - .try_fmt = mx2_camera_try_fmt, - .init_videobuf2 = mx2_camera_init_videobuf, - .poll = mx2_camera_poll, - .querycap = mx2_camera_querycap, - .set_bus_param = mx2_camera_set_bus_param, -}; - -static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, - int bufnum, bool err) -{ -#ifdef DEBUG - struct mx2_fmt_cfg *prp = pcdev->emma_prp; -#endif - struct mx2_buf_internal *ibuf; - struct mx2_buffer *buf; - struct vb2_buffer *vb; - struct vb2_v4l2_buffer *vbuf; - unsigned long phys; - - ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal, - queue); - - BUG_ON(ibuf->bufnum != bufnum); - - if (ibuf->discard) { - /* - * Discard buffer must not be returned to user space. - * Just return it to the discard queue. - */ - list_move_tail(pcdev->active_bufs.next, &pcdev->discard); - } else { - buf = mx2_ibuf_to_buf(ibuf); - - vb = &buf->vb.vb2_buf; - vbuf = to_vb2_v4l2_buffer(vb); -#ifdef DEBUG - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - if (prp->cfg.channel == 1) { - if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR + - 4 * bufnum) != phys) { - dev_err(pcdev->dev, "%lx != %x\n", phys, - readl(pcdev->base_emma + - PRP_DEST_RGB1_PTR + 4 * bufnum)); - } - } else { - if (readl(pcdev->base_emma + PRP_DEST_Y_PTR - - 0x14 * bufnum) != phys) { - dev_err(pcdev->dev, "%lx != %x\n", phys, - readl(pcdev->base_emma + - PRP_DEST_Y_PTR - 0x14 * bufnum)); - } - } -#endif - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, - vb2_plane_vaddr(vb, 0), - vb2_get_plane_payload(vb, 0)); - - list_del_init(&buf->internal.queue); - vb->timestamp = ktime_get_ns(); - vbuf->sequence = pcdev->frame_count; - if (err) - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); - else - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - } - - pcdev->frame_count++; - - if (list_empty(&pcdev->capture)) { - if (list_empty(&pcdev->discard)) { - dev_warn(pcdev->dev, "%s: trying to access empty discard list\n", - __func__); - return; - } - - ibuf = list_first_entry(&pcdev->discard, - struct mx2_buf_internal, queue); - ibuf->bufnum = bufnum; - - list_move_tail(pcdev->discard.next, &pcdev->active_bufs); - mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum); - return; - } - - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - - buf->internal.bufnum = bufnum; - - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - vb = &buf->vb.vb2_buf; - - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, bufnum); -} - -static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data) -{ - struct mx2_camera_dev *pcdev = data; - unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS); - struct mx2_buf_internal *ibuf; - - spin_lock(&pcdev->lock); - - if (list_empty(&pcdev->active_bufs)) { - dev_warn(pcdev->dev, "%s: called while active list is empty\n", - __func__); - - if (!status) { - spin_unlock(&pcdev->lock); - return IRQ_NONE; - } - } - - if (status & (1 << 7)) { /* overflow */ - u32 cntl = readl(pcdev->base_emma + PRP_CNTL); - writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN), - pcdev->base_emma + PRP_CNTL); - writel(cntl, pcdev->base_emma + PRP_CNTL); - - ibuf = list_first_entry(&pcdev->active_bufs, - struct mx2_buf_internal, queue); - mx27_camera_frame_done_emma(pcdev, - ibuf->bufnum, true); - - status &= ~(1 << 7); - } else if (((status & (3 << 5)) == (3 << 5)) || - ((status & (3 << 3)) == (3 << 3))) { - /* - * Both buffers have triggered, process the one we're expecting - * to first - */ - ibuf = list_first_entry(&pcdev->active_bufs, - struct mx2_buf_internal, queue); - mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false); - status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */ - } else if ((status & (1 << 6)) || (status & (1 << 4))) { - mx27_camera_frame_done_emma(pcdev, 0, false); - } else if ((status & (1 << 5)) || (status & (1 << 3))) { - mx27_camera_frame_done_emma(pcdev, 1, false); - } - - spin_unlock(&pcdev->lock); - writel(status, pcdev->base_emma + PRP_INTRSTATUS); - - return IRQ_HANDLED; -} - -static int mx27_camera_emma_init(struct platform_device *pdev) -{ - struct mx2_camera_dev *pcdev = platform_get_drvdata(pdev); - struct resource *res_emma; - int irq_emma; - int err = 0; - - res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1); - irq_emma = platform_get_irq(pdev, 1); - if (!res_emma || !irq_emma) { - dev_err(pcdev->dev, "no EMMA resources\n"); - err = -ENODEV; - goto out; - } - - pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma); - if (IS_ERR(pcdev->base_emma)) { - err = PTR_ERR(pcdev->base_emma); - goto out; - } - - err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0, - MX2_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(pcdev->dev, "Camera EMMA interrupt register failed\n"); - goto out; - } - - pcdev->clk_emma_ipg = devm_clk_get(pcdev->dev, "emma-ipg"); - if (IS_ERR(pcdev->clk_emma_ipg)) { - err = PTR_ERR(pcdev->clk_emma_ipg); - goto out; - } - - clk_prepare_enable(pcdev->clk_emma_ipg); - - pcdev->clk_emma_ahb = devm_clk_get(pcdev->dev, "emma-ahb"); - if (IS_ERR(pcdev->clk_emma_ahb)) { - err = PTR_ERR(pcdev->clk_emma_ahb); - goto exit_clk_emma_ipg; - } - - clk_prepare_enable(pcdev->clk_emma_ahb); - - err = mx27_camera_emma_prp_reset(pcdev); - if (err) - goto exit_clk_emma_ahb; - - return err; - -exit_clk_emma_ahb: - clk_disable_unprepare(pcdev->clk_emma_ahb); -exit_clk_emma_ipg: - clk_disable_unprepare(pcdev->clk_emma_ipg); -out: - return err; -} - -static int mx2_camera_probe(struct platform_device *pdev) -{ - struct mx2_camera_dev *pcdev; - struct resource *res_csi; - int irq_csi; - int err = 0; - - dev_dbg(&pdev->dev, "initialising\n"); - - res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq_csi = platform_get_irq(pdev, 0); - if (res_csi == NULL || irq_csi < 0) { - dev_err(&pdev->dev, "Missing platform resources data\n"); - err = -ENODEV; - goto exit; - } - - pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); - if (!pcdev) { - dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit; - } - - pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(pcdev->clk_csi_ahb)) { - dev_err(&pdev->dev, "Could not get csi ahb clock\n"); - err = PTR_ERR(pcdev->clk_csi_ahb); - goto exit; - } - - pcdev->clk_csi_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(pcdev->clk_csi_per)) { - dev_err(&pdev->dev, "Could not get csi per clock\n"); - err = PTR_ERR(pcdev->clk_csi_per); - goto exit; - } - - pcdev->pdata = pdev->dev.platform_data; - if (pcdev->pdata) { - long rate; - - pcdev->platform_flags = pcdev->pdata->flags; - - rate = clk_round_rate(pcdev->clk_csi_per, - pcdev->pdata->clk * 2); - if (rate <= 0) { - err = -ENODEV; - goto exit; - } - err = clk_set_rate(pcdev->clk_csi_per, rate); - if (err < 0) - goto exit; - } - - INIT_LIST_HEAD(&pcdev->capture); - INIT_LIST_HEAD(&pcdev->active_bufs); - INIT_LIST_HEAD(&pcdev->discard); - spin_lock_init(&pcdev->lock); - - pcdev->base_csi = devm_ioremap_resource(&pdev->dev, res_csi); - if (IS_ERR(pcdev->base_csi)) { - err = PTR_ERR(pcdev->base_csi); - goto exit; - } - - pcdev->dev = &pdev->dev; - platform_set_drvdata(pdev, pcdev); - - err = mx27_camera_emma_init(pdev); - if (err) - goto exit; - - /* - * We're done with drvdata here. Clear the pointer so that - * v4l2 core can start using drvdata on its purpose. - */ - platform_set_drvdata(pdev, NULL); - - pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME, - pcdev->soc_host.ops = &mx2_soc_camera_host_ops, - pcdev->soc_host.priv = pcdev; - pcdev->soc_host.v4l2_dev.dev = &pdev->dev; - pcdev->soc_host.nr = pdev->id; - - pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pcdev->alloc_ctx)) { - err = PTR_ERR(pcdev->alloc_ctx); - goto eallocctx; - } - err = soc_camera_host_register(&pcdev->soc_host); - if (err) - goto exit_free_emma; - - dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n", - clk_get_rate(pcdev->clk_csi_per)); - - return 0; - -exit_free_emma: - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); -eallocctx: - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); -exit: - return err; -} - -static int mx2_camera_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct mx2_camera_dev *pcdev = container_of(soc_host, - struct mx2_camera_dev, soc_host); - - soc_camera_host_unregister(&pcdev->soc_host); - - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); - - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); - - dev_info(&pdev->dev, "MX2 Camera driver unloaded\n"); - - return 0; -} - -static struct platform_driver mx2_camera_driver = { - .driver = { - .name = MX2_CAM_DRV_NAME, - }, - .id_table = mx2_camera_devtype, - .remove = mx2_camera_remove, -}; - -module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe); - -MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); -MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(MX2_CAM_VERSION); diff --git a/drivers/staging/media/mx3/Kconfig b/drivers/staging/media/mx3/Kconfig deleted file mode 100644 index 595d5fe7cad1..000000000000 --- a/drivers/staging/media/mx3/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config VIDEO_MX3 - tristate "i.MX3x Camera Sensor Interface driver" - depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA - depends on MX3_IPU || COMPILE_TEST - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - ---help--- - This is a v4l2 driver for the i.MX3x Camera Sensor Interface - - This driver is deprecated: it should become a stand-alone driver - instead of using the soc-camera framework. - - Unless someone is willing to take this on (unlikely with such - ancient hardware) it is going to be removed from the kernel - soon. diff --git a/drivers/staging/media/mx3/Makefile b/drivers/staging/media/mx3/Makefile deleted file mode 100644 index 6d91dcd80c1d..000000000000 --- a/drivers/staging/media/mx3/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# Makefile for i.MX3x Camera Sensor driver - -obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o diff --git a/drivers/staging/media/mx3/TODO b/drivers/staging/media/mx3/TODO deleted file mode 100644 index bc68fa443a3e..000000000000 --- a/drivers/staging/media/mx3/TODO +++ /dev/null @@ -1,10 +0,0 @@ -This driver is deprecated: it should become a stand-alone driver instead of -using the soc-camera framework. - -Unless someone is willing to take this on (unlikely with such ancient -hardware) it is going to be removed from the kernel soon. - -Note that trivial patches will not be accepted anymore, only a full conversion. - -If you want to convert this driver, please contact the linux-media mailinglist -(see http://linuxtv.org/lists.php). diff --git a/drivers/staging/media/mx3/mx3_camera.c b/drivers/staging/media/mx3/mx3_camera.c deleted file mode 100644 index aa39e9569b1a..000000000000 --- a/drivers/staging/media/mx3/mx3_camera.c +++ /dev/null @@ -1,1264 +0,0 @@ -/* - * V4L2 Driver for i.MX3x camera host - * - * Copyright (C) 2008 - * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/videodev2.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/vmalloc.h> -#include <linux/interrupt.h> -#include <linux/sched.h> -#include <linux/dma/ipu-dma.h> - -#include <media/v4l2-common.h> -#include <media/v4l2-dev.h> -#include <media/videobuf2-dma-contig.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> - -#include <linux/platform_data/media/camera-mx3.h> -#include <linux/platform_data/dma-imx.h> - -#define MX3_CAM_DRV_NAME "mx3-camera" - -/* CMOS Sensor Interface Registers */ -#define CSI_REG_START 0x60 - -#define CSI_SENS_CONF (0x60 - CSI_REG_START) -#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START) -#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START) -#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START) -#define CSI_TST_CTRL (0x70 - CSI_REG_START) -#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START) -#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START) -#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START) -#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START) -#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START) - -#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 -#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 -#define CSI_SENS_CONF_DATA_POL_SHIFT 2 -#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 -#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 -#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7 -#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 -#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10 -#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 -#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 - -#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT) -#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT) -#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT) - -#define MAX_VIDEO_MEM 16 - -struct mx3_camera_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct list_head queue; - - /* One descriptot per scatterlist (per frame) */ - struct dma_async_tx_descriptor *txd; - - /* We have to "build" a scatterlist ourselves - one element per frame */ - struct scatterlist sg; -}; - -/** - * struct mx3_camera_dev - i.MX3x camera (CSI) object - * @dev: camera device, to which the coherent buffer is attached - * @icd: currently attached camera sensor - * @clk: pointer to clock - * @base: remapped register base address - * @pdata: platform data - * @platform_flags: platform flags - * @mclk: master clock frequency in Hz - * @capture: list of capture videobuffers - * @lock: protects video buffer lists - * @active: active video buffer - * @idmac_channel: array of pointers to IPU DMAC DMA channels - * @soc_host: embedded soc_host object - */ -struct mx3_camera_dev { - /* - * i.MX3x is only supposed to handle one camera on its Camera Sensor - * Interface. If anyone ever builds hardware to enable more than one - * camera _simultaneously_, they will have to modify this driver too - */ - struct clk *clk; - - void __iomem *base; - - struct mx3_camera_pdata *pdata; - - unsigned long platform_flags; - unsigned long mclk; - u16 width_flags; /* max 15 bits */ - - struct list_head capture; - spinlock_t lock; /* Protects video buffer lists */ - struct mx3_camera_buffer *active; - size_t buf_total; - struct vb2_alloc_ctx *alloc_ctx; - enum v4l2_field field; - int sequence; - - /* IDMAC / dmaengine interface */ - struct idmac_channel *idmac_channel[1]; /* We need one channel */ - - struct soc_camera_host soc_host; -}; - -struct dma_chan_request { - struct mx3_camera_dev *mx3_cam; - enum ipu_channel id; -}; - -static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg) -{ - return __raw_readl(mx3->base + reg); -} - -static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg) -{ - __raw_writel(value, mx3->base + reg); -} - -static struct mx3_camera_buffer *to_mx3_vb(struct vb2_v4l2_buffer *vb) -{ - return container_of(vb, struct mx3_camera_buffer, vb); -} - -/* Called from the IPU IDMAC ISR */ -static void mx3_cam_dma_done(void *arg) -{ - struct idmac_tx_desc *desc = to_tx_desc(arg); - struct dma_chan *chan = desc->txd.chan; - struct idmac_channel *ichannel = to_idmac_chan(chan); - struct mx3_camera_dev *mx3_cam = ichannel->client; - - dev_dbg(chan->device->dev, "callback cookie %d, active DMA %pad\n", - desc->txd.cookie, mx3_cam->active ? &sg_dma_address(&mx3_cam->active->sg) : NULL); - - spin_lock(&mx3_cam->lock); - if (mx3_cam->active) { - struct vb2_v4l2_buffer *vb = &mx3_cam->active->vb; - struct mx3_camera_buffer *buf = to_mx3_vb(vb); - - list_del_init(&buf->queue); - vb->vb2_buf.timestamp = ktime_get_ns(); - vb->field = mx3_cam->field; - vb->sequence = mx3_cam->sequence++; - vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE); - } - - if (list_empty(&mx3_cam->capture)) { - mx3_cam->active = NULL; - spin_unlock(&mx3_cam->lock); - - /* - * stop capture - without further buffers IPU_CHA_BUF0_RDY will - * not get updated - */ - return; - } - - mx3_cam->active = list_entry(mx3_cam->capture.next, - struct mx3_camera_buffer, queue); - spin_unlock(&mx3_cam->lock); -} - -/* - * Videobuf operations - */ - -/* - * Calculate the __buffer__ (not data) size and number of buffers. - */ -static int mx3_videobuf_setup(struct vb2_queue *vq, - unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - - if (!mx3_cam->idmac_channel[0]) - return -EINVAL; - - alloc_ctxs[0] = mx3_cam->alloc_ctx; - - if (!vq->num_buffers) - mx3_cam->sequence = 0; - - if (!*count) - *count = 2; - - /* Called from VIDIOC_REQBUFS or in compatibility mode */ - if (!*num_planes) - sizes[0] = icd->sizeimage; - else if (sizes[0] < icd->sizeimage) - return -EINVAL; - - /* If *num_planes != 0, we have already verified *count. */ - if (sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) / - sizes[0]; - - *num_planes = 1; - - return 0; -} - -static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) -{ - /* Add more formats as need arises and test possibilities appear... */ - switch (fourcc) { - case V4L2_PIX_FMT_RGB24: - return IPU_PIX_FMT_RGB24; - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_RGB565: - default: - return IPU_PIX_FMT_GENERIC; - } -} - -static void mx3_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); - struct scatterlist *sg = &buf->sg; - struct dma_async_tx_descriptor *txd; - struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - struct idmac_video_param *video = &ichan->params.video; - const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt; - dma_cookie_t cookie; - size_t new_size; - - new_size = icd->sizeimage; - - if (vb2_plane_size(vb, 0) < new_size) { - dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n", - vbuf->vb2_buf.index, vb2_plane_size(vb, 0), new_size); - goto error; - } - - if (!buf->txd) { - sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0); - sg_dma_len(sg) = new_size; - - txd = dmaengine_prep_slave_sg( - &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); - if (!txd) - goto error; - - txd->callback_param = txd; - txd->callback = mx3_cam_dma_done; - - buf->txd = txd; - } else { - txd = buf->txd; - } - - vb2_set_plane_payload(vb, 0, new_size); - - /* This is the configuration of one sg-element */ - video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc); - - if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) { - /* - * If the IPU DMA channel is configured to transfer generic - * 8-bit data, we have to set up the geometry parameters - * correctly, according to the current pixel format. The DMA - * horizontal parameters in this case are expressed in bytes, - * not in pixels. - */ - video->out_width = icd->bytesperline; - video->out_height = icd->user_height; - video->out_stride = icd->bytesperline; - } else { - /* - * For IPU known formats the pixel unit will be managed - * successfully by the IPU code - */ - video->out_width = icd->user_width; - video->out_height = icd->user_height; - video->out_stride = icd->user_width; - } - -#ifdef DEBUG - /* helps to see what DMA actually has written */ - if (vb2_plane_vaddr(vb, 0)) - memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0)); -#endif - - spin_lock_irq(&mx3_cam->lock); - list_add_tail(&buf->queue, &mx3_cam->capture); - - if (!mx3_cam->active) - mx3_cam->active = buf; - - spin_unlock_irq(&mx3_cam->lock); - - cookie = txd->tx_submit(txd); - dev_dbg(icd->parent, "Submitted cookie %d DMA %pad\n", - cookie, &sg_dma_address(&buf->sg)); - - if (cookie >= 0) - return; - - spin_lock_irq(&mx3_cam->lock); - - /* Submit error */ - list_del_init(&buf->queue); - - if (mx3_cam->active == buf) - mx3_cam->active = NULL; - - spin_unlock_irq(&mx3_cam->lock); -error: - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -} - -static void mx3_videobuf_release(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); - struct dma_async_tx_descriptor *txd = buf->txd; - unsigned long flags; - - dev_dbg(icd->parent, - "Release%s DMA %pad, queue %sempty\n", - mx3_cam->active == buf ? " active" : "", &sg_dma_address(&buf->sg), - list_empty(&buf->queue) ? "" : "not "); - - spin_lock_irqsave(&mx3_cam->lock, flags); - - if (mx3_cam->active == buf) - mx3_cam->active = NULL; - - /* Doesn't hurt also if the list is empty */ - list_del_init(&buf->queue); - - if (txd) { - buf->txd = NULL; - if (mx3_cam->idmac_channel[0]) - async_tx_ack(txd); - } - - spin_unlock_irqrestore(&mx3_cam->lock, flags); - - mx3_cam->buf_total -= vb2_plane_size(vb, 0); -} - -static int mx3_videobuf_init(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); - - if (!buf->txd) { - /* This is for locking debugging only */ - INIT_LIST_HEAD(&buf->queue); - sg_init_table(&buf->sg, 1); - - mx3_cam->buf_total += vb2_plane_size(vb, 0); - } - - return 0; -} - -static void mx3_stop_streaming(struct vb2_queue *q) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - struct mx3_camera_buffer *buf, *tmp; - unsigned long flags; - - if (ichan) - dmaengine_pause(&ichan->dma_chan); - - spin_lock_irqsave(&mx3_cam->lock, flags); - - mx3_cam->active = NULL; - - list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) { - list_del_init(&buf->queue); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - spin_unlock_irqrestore(&mx3_cam->lock, flags); -} - -static struct vb2_ops mx3_videobuf_ops = { - .queue_setup = mx3_videobuf_setup, - .buf_queue = mx3_videobuf_queue, - .buf_cleanup = mx3_videobuf_release, - .buf_init = mx3_videobuf_init, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .stop_streaming = mx3_stop_streaming, -}; - -static int mx3_camera_init_videobuf(struct vb2_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = icd; - q->ops = &mx3_videobuf_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct mx3_camera_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ici->host_lock; - - return vb2_queue_init(q); -} - -/* First part of ipu_csi_init_interface() */ -static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam) -{ - u32 conf; - long rate; - - /* Set default size: ipu_csi_set_window_size() */ - csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE); - /* ...and position to 0:0: ipu_csi_set_window_pos() */ - conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; - csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL); - - /* We use only gated clock synchronisation mode so far */ - conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT; - - /* Set generic data, platform-biggest bus-width */ - conf |= CSI_SENS_CONF_DATA_FMT_BAYER; - - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) - conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) - conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) - conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/ - conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - - if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC) - conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC) - conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_DP) - conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_PCP) - conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_HSP) - conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_VSP) - conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; - - /* ipu_csi_init_interface() */ - csi_reg_write(mx3_cam, conf, CSI_SENS_CONF); - - clk_prepare_enable(mx3_cam->clk); - rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); - dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate); - if (rate) - clk_set_rate(mx3_cam->clk, rate); -} - -static int mx3_camera_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void mx3_camera_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n", - icd->devnum); -} - -/* Called with .host_lock held */ -static int mx3_camera_clock_start(struct soc_camera_host *ici) -{ - struct mx3_camera_dev *mx3_cam = ici->priv; - - mx3_camera_activate(mx3_cam); - - mx3_cam->buf_total = 0; - - return 0; -} - -/* Called with .host_lock held */ -static void mx3_camera_clock_stop(struct soc_camera_host *ici) -{ - struct mx3_camera_dev *mx3_cam = ici->priv; - struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; - - if (*ichan) { - dma_release_channel(&(*ichan)->dma_chan); - *ichan = NULL; - } - - clk_disable_unprepare(mx3_cam->clk); -} - -static int test_platform_param(struct mx3_camera_dev *mx3_cam, - unsigned char buswidth, unsigned long *flags) -{ - /* - * If requested data width is supported by the platform, use it or any - * possible lower value - i.MX31 is smart enough to shift bits - */ - if (buswidth > fls(mx3_cam->width_flags)) - return -EINVAL; - - /* - * Platform specified synchronization and pixel clock polarities are - * only a recommendation and are only used during probing. MX3x - * camera interface only works in master mode, i.e., uses HSYNC and - * VSYNC signals from the sensor - */ - *flags = V4L2_MBUS_MASTER | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_LOW; - - return 0; -} - -static int mx3_camera_try_bus_param(struct soc_camera_device *icd, - const unsigned int depth) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long bus_flags, common_flags; - int ret = test_platform_param(mx3_cam, depth, &bus_flags); - - dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret); - - if (ret < 0) - return ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, - bus_flags); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%lx\n", - cfg.flags, bus_flags); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } - - return 0; -} - -static bool chan_filter(struct dma_chan *chan, void *arg) -{ - struct dma_chan_request *rq = arg; - struct mx3_camera_pdata *pdata; - - if (!imx_dma_is_ipu(chan)) - return false; - - if (!rq) - return false; - - pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data; - - return rq->id == chan->chan_id && - pdata->dma_dev == chan->device->dev; -} - -static const struct soc_mbus_pixelfmt mx3_camera_formats[] = { - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .name = "Bayer BGGR (sRGB) 8 bit", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .name = "Monochrome 8 bit", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}; - -/* This will be corrected as we get more formats */ -static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt) -{ - return fmt->packing == SOC_MBUS_PACKING_NONE || - (fmt->bits_per_sample == 8 && - fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || - (fmt->bits_per_sample > 8 && - fmt->packing == SOC_MBUS_PACKING_EXTEND16); -} - -static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx, - struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - int formats = 0, ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* No more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(icd->parent, - "Unsupported format code #%u: 0x%x\n", idx, code.code); - return 0; - } - - /* This also checks support for the requested bits-per-sample */ - ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample); - if (ret < 0) - return 0; - - switch (code.code) { - case MEDIA_BUS_FMT_SBGGR10_1X10: - formats++; - if (xlate) { - xlate->host_fmt = &mx3_camera_formats[0]; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s using code 0x%x\n", - mx3_camera_formats[0].name, code.code); - } - break; - case MEDIA_BUS_FMT_Y10_1X10: - formats++; - if (xlate) { - xlate->host_fmt = &mx3_camera_formats[1]; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s using code 0x%x\n", - mx3_camera_formats[1].name, code.code); - } - break; - default: - if (!mx3_camera_packing_supported(fmt)) - return 0; - } - - /* Generic pass-through */ - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n", - (fmt->fourcc >> (0*8)) & 0xFF, - (fmt->fourcc >> (1*8)) & 0xFF, - (fmt->fourcc >> (2*8)) & 0xFF, - (fmt->fourcc >> (3*8)) & 0xFF); - xlate++; - } - - return formats; -} - -static void configure_geometry(struct mx3_camera_dev *mx3_cam, - unsigned int width, unsigned int height, - const struct soc_mbus_pixelfmt *fmt) -{ - u32 ctrl, width_field, height_field; - - if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) { - /* - * As the CSI will be configured to output BAYER, here - * the width parameter count the number of samples to - * capture to complete the whole image width. - */ - unsigned int num, den; - int ret = soc_mbus_samples_per_pixel(fmt, &num, &den); - BUG_ON(ret < 0); - width = width * num / den; - } - - /* Setup frame size - this cannot be changed on-the-fly... */ - width_field = width - 1; - height_field = height - 1; - csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE); - - csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1); - csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2); - - csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE); - - /* ...and position */ - ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; - /* Sensor does the cropping */ - csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL); -} - -static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) -{ - dma_cap_mask_t mask; - struct dma_chan *chan; - struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; - /* We have to use IDMAC_IC_7 for Bayer / generic data */ - struct dma_chan_request rq = {.mx3_cam = mx3_cam, - .id = IDMAC_IC_7}; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_PRIVATE, mask); - chan = dma_request_channel(mask, chan_filter, &rq); - if (!chan) - return -EBUSY; - - *ichan = to_idmac_chan(chan); - (*ichan)->client = mx3_cam; - - return 0; -} - -/* - * FIXME: learn to use stride != width, then we can keep stride properly aligned - * and support arbitrary (even) widths. - */ -static inline void stride_align(__u32 *width) -{ - if (ALIGN(*width, 8) < 4096) - *width = ALIGN(*width, 8); - else - *width = *width & ~7; -} - -/* - * As long as we don't implement host-side cropping and scaling, we can use - * default g_crop and cropcap from soc_camera.c - */ -static int mx3_camera_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) -{ - struct v4l2_crop a_writable = *a; - struct v4l2_rect *rect = &a_writable.c; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); - soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096); - - ret = v4l2_subdev_call(sd, video, s_crop, a); - if (ret < 0) - return ret; - - /* The capture device might have changed its output sizes */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - if (mf->code != icd->current_fmt->code) - return -EINVAL; - - if (mf->width & 7) { - /* Ouch! We can only handle 8-byte aligned width... */ - stride_align(&mf->width); - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); - if (ret < 0) - return ret; - } - - if (mf->width != icd->user_width || mf->height != icd->user_height) - configure_geometry(mx3_cam, mf->width, mf->height, - icd->current_fmt->host_fmt); - - dev_dbg(icd->parent, "Sensor cropped %dx%d\n", - mf->width, mf->height); - - icd->user_width = mf->width; - icd->user_height = mf->height; - - return ret; -} - -static int mx3_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", - pix->pixelformat); - return -EINVAL; - } - - stride_align(&pix->width); - dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height); - - /* - * Might have to perform a complete interface initialisation like in - * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider - * mxc_v4l2_s_fmt() - */ - - configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt); - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); - if (ret < 0) - return ret; - - if (mf->code != xlate->code) - return -EINVAL; - - if (!mx3_cam->idmac_channel[0]) { - ret = acquire_dma_channel(mx3_cam); - if (ret < 0) - return ret; - } - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - mx3_cam->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; - - dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height); - - return ret; -} - -static int mx3_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (pixfmt && !xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - /* limit to MX3 hardware capabilities */ - if (pix->height > 4096) - pix->height = 4096; - if (pix->width > 4096) - pix->width = 4096; - - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - pix->width = mf->width; - pix->height = mf->height; - pix->colorspace = mf->colorspace; - - switch (mf->field) { - case V4L2_FIELD_ANY: - pix->field = V4L2_FIELD_NONE; - break; - case V4L2_FIELD_NONE: - break; - default: - dev_err(icd->parent, "Field type %d unsupported.\n", - mf->field); - ret = -EINVAL; - } - - return ret; -} - -static int mx3_camera_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - return 0; -} - -static unsigned int mx3_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_poll(&icd->vb2_vidq, file, pt); -} - -static int mx3_camera_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the firendly caller:-> */ - strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int mx3_camera_set_bus_param(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - u32 pixfmt = icd->current_fmt->host_fmt->fourcc; - unsigned long bus_flags, common_flags; - u32 dw, sens_conf; - const struct soc_mbus_pixelfmt *fmt; - int buswidth; - int ret; - const struct soc_camera_format_xlate *xlate; - struct device *dev = icd->parent; - - fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code); - if (!fmt) - return -EINVAL; - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - dev_warn(dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - buswidth = fmt->bits_per_sample; - ret = test_platform_param(mx3_cam, buswidth, &bus_flags); - - dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret); - - if (ret < 0) - return ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, - bus_flags); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%lx\n", - cfg.flags, bus_flags); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = bus_flags; - } - - dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n", - cfg.flags, bus_flags, common_flags); - - /* Make choices, based on platform preferences */ - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (mx3_cam->platform_flags & MX3_CAMERA_HSP) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (mx3_cam->platform_flags & MX3_CAMERA_VSP) - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) { - if (mx3_cam->platform_flags & MX3_CAMERA_DP) - common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (mx3_cam->platform_flags & MX3_CAMERA_PCP) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); - return ret; - } - - /* - * So far only gated clock mode is supported. Add a line - * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) | - * below and select the required mode when supporting other - * synchronisation protocols. - */ - sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) & - ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) | - (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) | - (1 << CSI_SENS_CONF_DATA_POL_SHIFT) | - (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) | - (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) | - (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT)); - - /* TODO: Support RGB and YUV formats */ - - /* This has been set in mx3_camera_activate(), but we clear it above */ - sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER; - - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; - if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; - if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; - if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW) - sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; - - /* Just do what we're asked to do */ - switch (xlate->host_fmt->bits_per_sample) { - case 4: - dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - break; - case 8: - dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - break; - case 10: - dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - break; - default: - /* - * Actually it can only be 15 now, default is just to silence - * compiler warnings - */ - case 15: - dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - } - - csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF); - - dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw); - - return 0; -} - -static struct soc_camera_host_ops mx3_soc_camera_host_ops = { - .owner = THIS_MODULE, - .add = mx3_camera_add_device, - .remove = mx3_camera_remove_device, - .clock_start = mx3_camera_clock_start, - .clock_stop = mx3_camera_clock_stop, - .set_crop = mx3_camera_set_crop, - .set_fmt = mx3_camera_set_fmt, - .try_fmt = mx3_camera_try_fmt, - .get_formats = mx3_camera_get_formats, - .init_videobuf2 = mx3_camera_init_videobuf, - .reqbufs = mx3_camera_reqbufs, - .poll = mx3_camera_poll, - .querycap = mx3_camera_querycap, - .set_bus_param = mx3_camera_set_bus_param, -}; - -static int mx3_camera_probe(struct platform_device *pdev) -{ - struct mx3_camera_pdata *pdata = pdev->dev.platform_data; - struct mx3_camera_dev *mx3_cam; - struct resource *res; - void __iomem *base; - int err = 0; - struct soc_camera_host *soc_host; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - if (!pdata) - return -EINVAL; - - mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL); - if (!mx3_cam) { - dev_err(&pdev->dev, "Could not allocate mx3 camera object\n"); - return -ENOMEM; - } - - mx3_cam->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(mx3_cam->clk)) - return PTR_ERR(mx3_cam->clk); - - mx3_cam->pdata = pdata; - mx3_cam->platform_flags = pdata->flags; - if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) { - /* - * Platform hasn't set available data widths. This is bad. - * Warn and use a default. - */ - dev_warn(&pdev->dev, "WARNING! Platform hasn't set available " - "data widths, using default 8 bit\n"); - mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8; - } - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4) - mx3_cam->width_flags = 1 << 3; - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) - mx3_cam->width_flags |= 1 << 7; - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) - mx3_cam->width_flags |= 1 << 9; - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) - mx3_cam->width_flags |= 1 << 14; - - mx3_cam->mclk = pdata->mclk_10khz * 10000; - if (!mx3_cam->mclk) { - dev_warn(&pdev->dev, - "mclk_10khz == 0! Please, fix your platform data. " - "Using default 20MHz\n"); - mx3_cam->mclk = 20000000; - } - - /* list of video-buffers */ - INIT_LIST_HEAD(&mx3_cam->capture); - spin_lock_init(&mx3_cam->lock); - - mx3_cam->base = base; - - soc_host = &mx3_cam->soc_host; - soc_host->drv_name = MX3_CAM_DRV_NAME; - soc_host->ops = &mx3_soc_camera_host_ops; - soc_host->priv = mx3_cam; - soc_host->v4l2_dev.dev = &pdev->dev; - soc_host->nr = pdev->id; - - mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(mx3_cam->alloc_ctx)) - return PTR_ERR(mx3_cam->alloc_ctx); - - if (pdata->asd_sizes) { - soc_host->asd = pdata->asd; - soc_host->asd_sizes = pdata->asd_sizes; - } - - err = soc_camera_host_register(soc_host); - if (err) - goto ecamhostreg; - - /* IDMAC interface */ - dmaengine_get(); - - return 0; - -ecamhostreg: - vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); - return err; -} - -static int mx3_camera_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct mx3_camera_dev *mx3_cam = container_of(soc_host, - struct mx3_camera_dev, soc_host); - - soc_camera_host_unregister(soc_host); - - /* - * The channel has either not been allocated, - * or should have been released - */ - if (WARN_ON(mx3_cam->idmac_channel[0])) - dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan); - - vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); - - dmaengine_put(); - - return 0; -} - -static struct platform_driver mx3_camera_driver = { - .driver = { - .name = MX3_CAM_DRV_NAME, - }, - .probe = mx3_camera_probe, - .remove = mx3_camera_remove, -}; - -module_platform_driver(mx3_camera_driver); - -MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.2.3"); -MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME); diff --git a/drivers/staging/media/omap1/Kconfig b/drivers/staging/media/omap1/Kconfig deleted file mode 100644 index 6cfab3a04ae1..000000000000 --- a/drivers/staging/media/omap1/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config VIDEO_OMAP1 - tristate "OMAP1 Camera Interface driver" - depends on VIDEO_DEV && SOC_CAMERA - depends on ARCH_OMAP1 - depends on HAS_DMA - select VIDEOBUF_DMA_CONTIG - select VIDEOBUF_DMA_SG - ---help--- - This is a v4l2 driver for the TI OMAP1 camera interface - - This driver is deprecated and will be removed soon unless someone - will start the work to convert this driver to the vb2 framework - and remove the soc-camera dependency. diff --git a/drivers/staging/media/omap1/Makefile b/drivers/staging/media/omap1/Makefile deleted file mode 100644 index 2885622600f2..000000000000 --- a/drivers/staging/media/omap1/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# Makefile for OMAP1 driver - -obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o diff --git a/drivers/staging/media/omap1/TODO b/drivers/staging/media/omap1/TODO deleted file mode 100644 index 1025f9f60ff0..000000000000 --- a/drivers/staging/media/omap1/TODO +++ /dev/null @@ -1,8 +0,0 @@ -This driver is deprecated and will be removed soon unless someone will start -the work to convert this driver to the vb2 framework and remove the -soc-camera dependency. - -Note that trivial patches will not be accepted anymore, only a full conversion. - -If you want to convert this driver, please contact the linux-media mailinglist -(see http://linuxtv.org/lists.php). diff --git a/drivers/staging/media/omap1/omap1_camera.c b/drivers/staging/media/omap1/omap1_camera.c deleted file mode 100644 index 54b8dd2d2bba..000000000000 --- a/drivers/staging/media/omap1/omap1_camera.c +++ /dev/null @@ -1,1702 +0,0 @@ -/* - * V4L2 SoC Camera driver for OMAP1 Camera Interface - * - * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> - * - * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host - * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt> - * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com> - * - * Based on PXA SoC camera driver - * Copyright (C) 2006, Sascha Hauer, Pengutronix - * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> - * - * Hardware specific bits initialy based on former work by Matt Callow - * drivers/media/platform/omap/omap1510cam.c - * Copyright (C) 2006 Matt Callow - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - - -#include <linux/clk.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -#include <linux/platform_data/media/omap1_camera.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> -#include <media/videobuf-dma-contig.h> -#include <media/videobuf-dma-sg.h> - -#include <linux/omap-dma.h> - - -#define DRIVER_NAME "omap1-camera" -#define DRIVER_VERSION "0.0.2" - -#define OMAP_DMA_CAMERA_IF_RX 20 - -/* - * --------------------------------------------------------------------------- - * OMAP1 Camera Interface registers - * --------------------------------------------------------------------------- - */ - -#define REG_CTRLCLOCK 0x00 -#define REG_IT_STATUS 0x04 -#define REG_MODE 0x08 -#define REG_STATUS 0x0C -#define REG_CAMDATA 0x10 -#define REG_GPIO 0x14 -#define REG_PEAK_COUNTER 0x18 - -/* CTRLCLOCK bit shifts */ -#define LCLK_EN BIT(7) -#define DPLL_EN BIT(6) -#define MCLK_EN BIT(5) -#define CAMEXCLK_EN BIT(4) -#define POLCLK BIT(3) -#define FOSCMOD_SHIFT 0 -#define FOSCMOD_MASK (0x7 << FOSCMOD_SHIFT) -#define FOSCMOD_12MHz 0x0 -#define FOSCMOD_6MHz 0x2 -#define FOSCMOD_9_6MHz 0x4 -#define FOSCMOD_24MHz 0x5 -#define FOSCMOD_8MHz 0x6 - -/* IT_STATUS bit shifts */ -#define DATA_TRANSFER BIT(5) -#define FIFO_FULL BIT(4) -#define H_DOWN BIT(3) -#define H_UP BIT(2) -#define V_DOWN BIT(1) -#define V_UP BIT(0) - -/* MODE bit shifts */ -#define RAZ_FIFO BIT(18) -#define EN_FIFO_FULL BIT(17) -#define EN_NIRQ BIT(16) -#define THRESHOLD_SHIFT 9 -#define THRESHOLD_MASK (0x7f << THRESHOLD_SHIFT) -#define DMA BIT(8) -#define EN_H_DOWN BIT(7) -#define EN_H_UP BIT(6) -#define EN_V_DOWN BIT(5) -#define EN_V_UP BIT(4) -#define ORDERCAMD BIT(3) - -#define IRQ_MASK (EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \ - EN_NIRQ | EN_FIFO_FULL) - -/* STATUS bit shifts */ -#define HSTATUS BIT(1) -#define VSTATUS BIT(0) - -/* GPIO bit shifts */ -#define CAM_RST BIT(0) - -/* end of OMAP1 Camera Interface registers */ - - -#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \ - V4L2_MBUS_DATA_ACTIVE_HIGH) - - -#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1) -#define FIFO_SHIFT __fls(FIFO_SIZE) - -#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4) -#define DMA_BURST_SIZE (1 << DMA_BURST_SHIFT) - -#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32 -#define DMA_ELEMENT_SIZE (1 << DMA_ELEMENT_SHIFT) - -#define DMA_FRAME_SHIFT_CONTIG (FIFO_SHIFT - 1) -#define DMA_FRAME_SHIFT_SG DMA_BURST_SHIFT - -#define DMA_FRAME_SHIFT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? \ - DMA_FRAME_SHIFT_CONTIG : \ - DMA_FRAME_SHIFT_SG) -#define DMA_FRAME_SIZE(x) (1 << DMA_FRAME_SHIFT(x)) -#define DMA_SYNC OMAP_DMA_SYNC_FRAME -#define THRESHOLD_LEVEL DMA_FRAME_SIZE - - -#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */ - - -/* - * Structures - */ - -/* buffer for one video frame */ -struct omap1_cam_buf { - struct videobuf_buffer vb; - u32 code; - int inwork; - struct scatterlist *sgbuf; - int sgcount; - int bytes_left; - enum videobuf_state result; -}; - -struct omap1_cam_dev { - struct soc_camera_host soc_host; - struct clk *clk; - - unsigned int irq; - void __iomem *base; - - int dma_ch; - - struct omap1_cam_platform_data *pdata; - struct resource *res; - unsigned long pflags; - unsigned long camexclk; - - struct list_head capture; - - /* lock used to protect videobuf */ - spinlock_t lock; - - /* Pointers to DMA buffers */ - struct omap1_cam_buf *active; - struct omap1_cam_buf *ready; - - enum omap1_cam_vb_mode vb_mode; - int (*mmap_mapper)(struct videobuf_queue *q, - struct videobuf_buffer *buf, - struct vm_area_struct *vma); - - u32 reg_cache[0]; -}; - - -static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val) -{ - pcdev->reg_cache[reg / sizeof(u32)] = val; - __raw_writel(val, pcdev->base + reg); -} - -static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache) -{ - return !from_cache ? __raw_readl(pcdev->base + reg) : - pcdev->reg_cache[reg / sizeof(u32)]; -} - -#define CAM_READ(pcdev, reg) \ - cam_read(pcdev, REG_##reg, false) -#define CAM_WRITE(pcdev, reg, val) \ - cam_write(pcdev, REG_##reg, val) -#define CAM_READ_CACHE(pcdev, reg) \ - cam_read(pcdev, REG_##reg, true) - -/* - * Videobuf operations - */ -static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - - *size = icd->sizeimage; - - if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode)) - *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode); - - if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; - - dev_dbg(icd->parent, - "%s: count=%d, size=%d\n", __func__, *count, *size); - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf, - enum omap1_cam_vb_mode vb_mode) -{ - struct videobuf_buffer *vb = &buf->vb; - - BUG_ON(in_interrupt()); - - videobuf_waiton(vq, vb, 0, 0); - - if (vb_mode == OMAP1_CAM_DMA_CONTIG) { - videobuf_dma_contig_free(vq, vb); - } else { - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->parent; - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - videobuf_dma_unmap(dev, dma); - videobuf_dma_free(dma); - } - - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static int omap1_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct soc_camera_device *icd = vq->priv_data; - struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - int ret; - - WARN_ON(!list_empty(&vb->queue)); - - BUG_ON(NULL == icd->current_fmt); - - buf->inwork = 1; - - if (buf->code != icd->current_fmt->code || vb->field != field || - vb->width != icd->user_width || - vb->height != icd->user_height) { - buf->code = icd->current_fmt->code; - vb->width = icd->user_width; - vb->height = icd->user_height; - vb->field = field; - vb->state = VIDEOBUF_NEEDS_INIT; - } - - vb->size = icd->sizeimage; - - if (vb->baddr && vb->bsize < vb->size) { - ret = -EINVAL; - goto out; - } - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - - vb->state = VIDEOBUF_PREPARED; - } - buf->inwork = 0; - - return 0; -fail: - free_buffer(vq, buf, pcdev->vb_mode); -out: - buf->inwork = 0; - return ret; -} - -static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf, - enum omap1_cam_vb_mode vb_mode) -{ - dma_addr_t dma_addr; - unsigned int block_size; - - if (vb_mode == OMAP1_CAM_DMA_CONTIG) { - dma_addr = videobuf_to_dma_contig(&buf->vb); - block_size = buf->vb.size; - } else { - if (WARN_ON(!buf->sgbuf)) { - buf->result = VIDEOBUF_ERROR; - return; - } - dma_addr = sg_dma_address(buf->sgbuf); - if (WARN_ON(!dma_addr)) { - buf->sgbuf = NULL; - buf->result = VIDEOBUF_ERROR; - return; - } - block_size = sg_dma_len(buf->sgbuf); - if (WARN_ON(!block_size)) { - buf->sgbuf = NULL; - buf->result = VIDEOBUF_ERROR; - return; - } - if (unlikely(buf->bytes_left < block_size)) - block_size = buf->bytes_left; - if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) * - DMA_ELEMENT_SIZE - 1))) { - dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) * - DMA_ELEMENT_SIZE); - block_size &= ~(DMA_FRAME_SIZE(vb_mode) * - DMA_ELEMENT_SIZE - 1); - } - buf->bytes_left -= block_size; - buf->sgcount++; - } - - omap_set_dma_dest_params(dma_ch, - OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0); - omap_set_dma_transfer_params(dma_ch, - OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode), - block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT), - DMA_SYNC, 0, 0); -} - -static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev) -{ - struct omap1_cam_buf *buf; - - /* - * If there is already a buffer pointed out by the pcdev->ready, - * (re)use it, otherwise try to fetch and configure a new one. - */ - buf = pcdev->ready; - if (!buf) { - if (list_empty(&pcdev->capture)) - return buf; - buf = list_entry(pcdev->capture.next, - struct omap1_cam_buf, vb.queue); - buf->vb.state = VIDEOBUF_ACTIVE; - pcdev->ready = buf; - list_del_init(&buf->vb.queue); - } - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, we can safely enter next buffer parameters - * into the DMA programming register set after the DMA - * has already been activated on the previous buffer - */ - set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode); - } else { - /* - * In SG mode, the above is not safe since there are probably - * a bunch of sgbufs from previous sglist still pending. - * Instead, mark the sglist fresh for the upcoming - * try_next_sgbuf(). - */ - buf->sgbuf = NULL; - } - - return buf; -} - -static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf) -{ - struct scatterlist *sgbuf; - - if (likely(buf->sgbuf)) { - /* current sglist is active */ - if (unlikely(!buf->bytes_left)) { - /* indicate sglist complete */ - sgbuf = NULL; - } else { - /* process next sgbuf */ - sgbuf = sg_next(buf->sgbuf); - if (WARN_ON(!sgbuf)) { - buf->result = VIDEOBUF_ERROR; - } else if (WARN_ON(!sg_dma_len(sgbuf))) { - sgbuf = NULL; - buf->result = VIDEOBUF_ERROR; - } - } - buf->sgbuf = sgbuf; - } else { - /* sglist is fresh, initialize it before using */ - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); - - sgbuf = dma->sglist; - if (!(WARN_ON(!sgbuf))) { - buf->sgbuf = sgbuf; - buf->sgcount = 0; - buf->bytes_left = buf->vb.size; - buf->result = VIDEOBUF_DONE; - } - } - if (sgbuf) - /* - * Put our next sgbuf parameters (address, size) - * into the DMA programming register set. - */ - set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG); - - return sgbuf; -} - -static void start_capture(struct omap1_cam_dev *pcdev) -{ - struct omap1_cam_buf *buf = pcdev->active; - u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN; - - if (WARN_ON(!buf)) - return; - - /* - * Enable start of frame interrupt, which we will use for activating - * our end of frame watchdog when capture actually starts. - */ - mode |= EN_V_UP; - - if (unlikely(ctrlclock & LCLK_EN)) - /* stop pixel clock before FIFO reset */ - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - /* reset FIFO */ - CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO); - - omap_start_dma(pcdev->dma_ch); - - if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { - /* - * In SG mode, it's a good moment for fetching next sgbuf - * from the current sglist and, if available, already putting - * its parameters into the DMA programming register set. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - } - - /* (re)enable pixel clock */ - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN); - /* release FIFO reset */ - CAM_WRITE(pcdev, MODE, mode); -} - -static void suspend_capture(struct omap1_cam_dev *pcdev) -{ - u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - omap_stop_dma(pcdev->dma_ch); -} - -static void disable_capture(struct omap1_cam_dev *pcdev) -{ - u32 mode = CAM_READ_CACHE(pcdev, MODE); - - CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA)); -} - -static void omap1_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - struct omap1_cam_buf *buf; - u32 mode; - - list_add_tail(&vb->queue, &pcdev->capture); - vb->state = VIDEOBUF_QUEUED; - - if (pcdev->active) { - /* - * Capture in progress, so don't touch pcdev->ready even if - * empty. Since the transfer of the DMA programming register set - * content to the DMA working register set is done automatically - * by the DMA hardware, this can pretty well happen while we - * are keeping the lock here. Leave fetching it from the queue - * to be done when a next DMA interrupt occures instead. - */ - return; - } - - WARN_ON(pcdev->ready); - - buf = prepare_next_vb(pcdev); - if (WARN_ON(!buf)) - return; - - pcdev->active = buf; - pcdev->ready = NULL; - - dev_dbg(icd->parent, - "%s: capture not active, setup FIFO, start DMA\n", __func__); - mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK; - mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT; - CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA); - - if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { - /* - * In SG mode, the above prepare_next_vb() didn't actually - * put anything into the DMA programming register set, - * so we have to do it now, before activating DMA. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - } - - start_capture(pcdev); -} - -static void omap1_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct omap1_cam_buf *buf = - container_of(vb, struct omap1_cam_buf, vb); - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - - switch (vb->state) { - case VIDEOBUF_DONE: - dev_dbg(dev, "%s (done)\n", __func__); - break; - case VIDEOBUF_ACTIVE: - dev_dbg(dev, "%s (active)\n", __func__); - break; - case VIDEOBUF_QUEUED: - dev_dbg(dev, "%s (queued)\n", __func__); - break; - case VIDEOBUF_PREPARED: - dev_dbg(dev, "%s (prepared)\n", __func__); - break; - default: - dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state); - break; - } - - free_buffer(vq, buf, pcdev->vb_mode); -} - -static void videobuf_done(struct omap1_cam_dev *pcdev, - enum videobuf_state result) -{ - struct omap1_cam_buf *buf = pcdev->active; - struct videobuf_buffer *vb; - struct device *dev = pcdev->soc_host.icd->parent; - - if (WARN_ON(!buf)) { - suspend_capture(pcdev); - disable_capture(pcdev); - return; - } - - if (result == VIDEOBUF_ERROR) - suspend_capture(pcdev); - - vb = &buf->vb; - if (waitqueue_active(&vb->done)) { - if (!pcdev->ready && result != VIDEOBUF_ERROR) { - /* - * No next buffer has been entered into the DMA - * programming register set on time (could be done only - * while the previous DMA interurpt was processed, not - * later), so the last DMA block, be it a whole buffer - * if in CONTIG or its last sgbuf if in SG mode, is - * about to be reused by the just autoreinitialized DMA - * engine, and overwritten with next frame data. Best we - * can do is stopping the capture as soon as possible, - * hopefully before the next frame start. - */ - suspend_capture(pcdev); - } - vb->state = result; - v4l2_get_timestamp(&vb->ts); - if (result != VIDEOBUF_ERROR) - vb->field_count++; - wake_up(&vb->done); - - /* shift in next buffer */ - buf = pcdev->ready; - pcdev->active = buf; - pcdev->ready = NULL; - - if (!buf) { - /* - * No next buffer was ready on time (see above), so - * indicate error condition to force capture restart or - * stop, depending on next buffer already queued or not. - */ - result = VIDEOBUF_ERROR; - prepare_next_vb(pcdev); - - buf = pcdev->ready; - pcdev->active = buf; - pcdev->ready = NULL; - } - } else if (pcdev->ready) { - /* - * In both CONTIG and SG mode, the DMA engine has possibly - * been already autoreinitialized with the preprogrammed - * pcdev->ready buffer. We can either accept this fact - * and just swap the buffers, or provoke an error condition - * and restart capture. The former seems less intrusive. - */ - dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n", - __func__); - pcdev->active = pcdev->ready; - - if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { - /* - * In SG mode, we have to make sure that the buffer we - * are putting back into the pcdev->ready is marked - * fresh. - */ - buf->sgbuf = NULL; - } - pcdev->ready = buf; - - buf = pcdev->active; - } else { - /* - * No next buffer has been entered into - * the DMA programming register set on time. - */ - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, the DMA engine has already been - * reinitialized with the current buffer. Best we can do - * is not touching it. - */ - dev_dbg(dev, - "%s: nobody waiting on videobuf, reuse it\n", - __func__); - } else { - /* - * In SG mode, the DMA engine has just been - * autoreinitialized with the last sgbuf from the - * current list. Restart capture in order to transfer - * next frame start into the first sgbuf, not the last - * one. - */ - if (result != VIDEOBUF_ERROR) { - suspend_capture(pcdev); - result = VIDEOBUF_ERROR; - } - } - } - - if (!buf) { - dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__); - disable_capture(pcdev); - return; - } - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, the current buffer parameters had already - * been entered into the DMA programming register set while the - * buffer was fetched with prepare_next_vb(), they may have also - * been transferred into the runtime set and already active if - * the DMA still running. - */ - } else { - /* In SG mode, extra steps are required */ - if (result == VIDEOBUF_ERROR) - /* make sure we (re)use sglist from start on error */ - buf->sgbuf = NULL; - - /* - * In any case, enter the next sgbuf parameters into the DMA - * programming register set. They will be used either during - * nearest DMA autoreinitialization or, in case of an error, - * on DMA startup below. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - } - - if (result == VIDEOBUF_ERROR) { - dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n", - __func__); - start_capture(pcdev); - /* - * In SG mode, the above also resulted in the next sgbuf - * parameters being entered into the DMA programming register - * set, making them ready for next DMA autoreinitialization. - */ - } - - /* - * Finally, try fetching next buffer. - * In CONTIG mode, it will also enter it into the DMA programming - * register set, making it ready for next DMA autoreinitialization. - */ - prepare_next_vb(pcdev); -} - -static void dma_isr(int channel, unsigned short status, void *data) -{ - struct omap1_cam_dev *pcdev = data; - struct omap1_cam_buf *buf = pcdev->active; - unsigned long flags; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (WARN_ON(!buf)) { - suspend_capture(pcdev); - disable_capture(pcdev); - goto out; - } - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, assume we have just managed to collect the - * whole frame, hopefully before our end of frame watchdog is - * triggered. Then, all we have to do is disabling the watchdog - * for this frame, and calling videobuf_done() with success - * indicated. - */ - CAM_WRITE(pcdev, MODE, - CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN); - videobuf_done(pcdev, VIDEOBUF_DONE); - } else { - /* - * In SG mode, we have to process every sgbuf from the current - * sglist, one after another. - */ - if (buf->sgbuf) { - /* - * Current sglist not completed yet, try fetching next - * sgbuf, hopefully putting it into the DMA programming - * register set, making it ready for next DMA - * autoreinitialization. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - if (buf->sgbuf) - goto out; - - /* - * No more sgbufs left in the current sglist. This - * doesn't mean that the whole videobuffer is already - * complete, but only that the last sgbuf from the - * current sglist is about to be filled. It will be - * ready on next DMA interrupt, signalled with the - * buf->sgbuf set back to NULL. - */ - if (buf->result != VIDEOBUF_ERROR) { - /* - * Video frame collected without errors so far, - * we can prepare for collecting a next one - * as soon as DMA gets autoreinitialized - * after the current (last) sgbuf is completed. - */ - buf = prepare_next_vb(pcdev); - if (!buf) - goto out; - - try_next_sgbuf(pcdev->dma_ch, buf); - goto out; - } - } - /* end of videobuf */ - videobuf_done(pcdev, buf->result); - } - -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static irqreturn_t cam_isr(int irq, void *data) -{ - struct omap1_cam_dev *pcdev = data; - struct device *dev = pcdev->soc_host.icd->parent; - struct omap1_cam_buf *buf = pcdev->active; - u32 it_status; - unsigned long flags; - - it_status = CAM_READ(pcdev, IT_STATUS); - if (!it_status) - return IRQ_NONE; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (WARN_ON(!buf)) { - dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n", - __func__, it_status); - suspend_capture(pcdev); - disable_capture(pcdev); - goto out; - } - - if (unlikely(it_status & FIFO_FULL)) { - dev_warn(dev, "%s: FIFO overflow\n", __func__); - - } else if (it_status & V_DOWN) { - /* end of video frame watchdog */ - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, the watchdog is disabled with - * successful DMA end of block interrupt, and reenabled - * on next frame start. If we get here, there is nothing - * to check, we must be out of sync. - */ - } else { - if (buf->sgcount == 2) { - /* - * If exactly 2 sgbufs from the next sglist have - * been programmed into the DMA engine (the - * first one already transferred into the DMA - * runtime register set, the second one still - * in the programming set), then we are in sync. - */ - goto out; - } - } - dev_notice(dev, "%s: unexpected end of video frame\n", - __func__); - - } else if (it_status & V_UP) { - u32 mode; - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, we need this interrupt every frame - * in oredr to reenable our end of frame watchdog. - */ - mode = CAM_READ_CACHE(pcdev, MODE); - } else { - /* - * In SG mode, the below enabled end of frame watchdog - * is kept on permanently, so we can turn this one shot - * setup off. - */ - mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP; - } - - if (!(mode & EN_V_DOWN)) { - /* (re)enable end of frame watchdog interrupt */ - mode |= EN_V_DOWN; - } - CAM_WRITE(pcdev, MODE, mode); - goto out; - - } else { - dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n", - __func__, it_status); - goto out; - } - - videobuf_done(pcdev, VIDEOBUF_ERROR); -out: - spin_unlock_irqrestore(&pcdev->lock, flags); - return IRQ_HANDLED; -} - -static struct videobuf_queue_ops omap1_videobuf_ops = { - .buf_setup = omap1_videobuf_setup, - .buf_prepare = omap1_videobuf_prepare, - .buf_queue = omap1_videobuf_queue, - .buf_release = omap1_videobuf_release, -}; - - -/* - * SOC Camera host operations - */ - -static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset) -{ - /* apply/release camera sensor reset if requested by platform data */ - if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH) - CAM_WRITE(pcdev, GPIO, reset); - else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW) - CAM_WRITE(pcdev, GPIO, !reset); -} - -static int omap1_cam_add_device(struct soc_camera_device *icd) -{ - dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void omap1_cam_remove_device(struct soc_camera_device *icd) -{ - dev_dbg(icd->parent, - "OMAP1 Camera driver detached from camera %d\n", icd->devnum); -} - -/* - * The following two functions absolutely depend on the fact, that - * there can be only one camera on OMAP1 camera sensor interface - */ -static int omap1_cam_clock_start(struct soc_camera_host *ici) -{ - struct omap1_cam_dev *pcdev = ici->priv; - u32 ctrlclock; - - clk_enable(pcdev->clk); - - /* setup sensor clock */ - ctrlclock = CAM_READ(pcdev, CTRLCLOCK); - ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN); - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - ctrlclock &= ~FOSCMOD_MASK; - switch (pcdev->camexclk) { - case 6000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz; - break; - case 8000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN; - break; - case 9600000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN; - break; - case 12000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz; - break; - case 24000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN; - default: - break; - } - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN); - - /* enable internal clock */ - ctrlclock |= MCLK_EN; - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - sensor_reset(pcdev, false); - - return 0; -} - -static void omap1_cam_clock_stop(struct soc_camera_host *ici) -{ - struct omap1_cam_dev *pcdev = ici->priv; - u32 ctrlclock; - - suspend_capture(pcdev); - disable_capture(pcdev); - - sensor_reset(pcdev, true); - - /* disable and release system clocks */ - ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN); - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz; - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN); - - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN); - - clk_disable(pcdev->clk); -} - -/* Duplicate standard formats based on host capability of byte swapping */ -static const struct soc_mbus_lookup omap1_cam_formats[] = { -{ - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555, - .name = "RGB555", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555X, - .name = "RGB555X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565, - .name = "RGB565", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565X, - .name = "RGB565X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, -}; - -static int omap1_cam_get_formats(struct soc_camera_device *icd, - unsigned int idx, struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - int formats = 0, ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* No more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__, - idx, code.code); - return 0; - } - - /* Check support for the requested bits-per-sample */ - if (fmt->bits_per_sample != 8) - return 0; - - switch (code.code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YVYU8_2X8: - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: - case MEDIA_BUS_FMT_RGB565_2X8_BE: - case MEDIA_BUS_FMT_RGB565_2X8_LE: - formats++; - if (xlate) { - xlate->host_fmt = soc_mbus_find_fmtdesc(code.code, - omap1_cam_formats, - ARRAY_SIZE(omap1_cam_formats)); - xlate->code = code.code; - xlate++; - dev_dbg(dev, - "%s: providing format %s as byte swapped code #%d\n", - __func__, xlate->host_fmt->name, code.code); - } - default: - if (xlate) - dev_dbg(dev, - "%s: providing format %s in pass-through mode\n", - __func__, fmt->name); - } - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - xlate++; - } - - return formats; -} - -static bool is_dma_aligned(s32 bytes_per_line, unsigned int height, - enum omap1_cam_vb_mode vb_mode) -{ - int size = bytes_per_line * height; - - return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) && - IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE); -} - -static int dma_align(int *width, int *height, - const struct soc_mbus_pixelfmt *fmt, - enum omap1_cam_vb_mode vb_mode, bool enlarge) -{ - s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt); - - if (bytes_per_line < 0) - return bytes_per_line; - - if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) { - unsigned int pxalign = __fls(bytes_per_line / *width); - unsigned int salign = DMA_FRAME_SHIFT(vb_mode) + - DMA_ELEMENT_SHIFT - pxalign; - unsigned int incr = enlarge << salign; - - v4l_bound_align_image(width, 1, *width + incr, 0, - height, 1, *height + incr, 0, salign); - return 0; - } - return 1; -} - -#define subdev_call_with_sense(pcdev, dev, icd, sd, op, function, args...) \ -({ \ - struct soc_camera_sense sense = { \ - .master_clock = pcdev->camexclk, \ - .pixel_clock_max = 0, \ - }; \ - int __ret; \ - \ - if (pcdev->pdata) \ - sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \ - icd->sense = &sense; \ - __ret = v4l2_subdev_call(sd, op, function, ##args); \ - icd->sense = NULL; \ - \ - if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \ - if (sense.pixel_clock > sense.pixel_clock_max) { \ - dev_err(dev, \ - "%s: pixel clock %lu set by the camera too high!\n", \ - __func__, sense.pixel_clock); \ - __ret = -EINVAL; \ - } \ - } \ - __ret; \ -}) - -static int set_format(struct omap1_cam_dev *pcdev, struct device *dev, - struct soc_camera_device *icd, struct v4l2_subdev *sd, - struct v4l2_subdev_format *format, - const struct soc_camera_format_xlate *xlate) -{ - s32 bytes_per_line; - struct v4l2_mbus_framefmt *mf = &format->format; - int ret = subdev_call_with_sense(pcdev, dev, icd, sd, pad, set_fmt, NULL, format); - - if (ret < 0) { - dev_err(dev, "%s: set_fmt failed\n", __func__); - return ret; - } - - if (mf->code != xlate->code) { - dev_err(dev, "%s: unexpected pixel code change\n", __func__); - return -EINVAL; - } - - bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt); - if (bytes_per_line < 0) { - dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n", - __func__); - return bytes_per_line; - } - - if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) { - dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n", - __func__, mf->width, mf->height); - return -EINVAL; - } - return 0; -} - -static int omap1_cam_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *crop) -{ - const struct v4l2_rect *rect = &crop->c; - const struct soc_camera_format_xlate *xlate = icd->current_fmt; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - ret = subdev_call_with_sense(pcdev, dev, icd, sd, video, s_crop, crop); - if (ret < 0) { - dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__, - rect->width, rect->height, rect->left, rect->top); - return ret; - } - - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) { - dev_warn(dev, "%s: failed to fetch current format\n", __func__); - return ret; - } - - ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode, - false); - if (ret < 0) { - dev_err(dev, "%s: failed to align %ux%u %s with DMA\n", - __func__, mf->width, mf->height, - xlate->host_fmt->name); - return ret; - } - - if (!ret) { - /* sensor returned geometry not DMA aligned, trying to fix */ - ret = set_format(pcdev, dev, icd, sd, &fmt, xlate); - if (ret < 0) { - dev_err(dev, "%s: failed to set format\n", __func__); - return ret; - } - } - - icd->user_width = mf->width; - icd->user_height = mf->height; - - return 0; -} - -static int omap1_cam_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(dev, "%s: format %#x not found\n", __func__, - pix->pixelformat); - return -EINVAL; - } - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode, - true); - if (ret < 0) { - dev_err(dev, "%s: failed to align %ux%u %s with DMA\n", - __func__, pix->width, pix->height, - xlate->host_fmt->name); - return ret; - } - - ret = set_format(pcdev, dev, icd, sd, &format, xlate); - if (ret < 0) { - dev_err(dev, "%s: failed to set format\n", __func__); - return ret; - } - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; - - return 0; -} - -static int omap1_cam_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - /* TODO: limit to mx1 hardware capabilities */ - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %#x not found\n", - pix->pixelformat); - return -EINVAL; - } - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - /* limit to sensor capabilities */ - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - - return 0; -} - -static bool sg_mode; - -/* - * Local mmap_mapper wrapper, - * used for detecting videobuf-dma-contig buffer allocation failures - * and switching to videobuf-dma-sg automatically for future attempts. - */ -static int omap1_cam_mmap_mapper(struct videobuf_queue *q, - struct videobuf_buffer *buf, - struct vm_area_struct *vma) -{ - struct soc_camera_device *icd = q->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - int ret; - - ret = pcdev->mmap_mapper(q, buf, vma); - - if (ret == -ENOMEM) - sg_mode = true; - - return ret; -} - -static void omap1_cam_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - - if (!sg_mode) - videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops, - icd->parent, &pcdev->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &ici->host_lock); - else - videobuf_queue_sg_init(q, &omap1_videobuf_ops, - icd->parent, &pcdev->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &ici->host_lock); - - /* use videobuf mode (auto)selected with the module parameter */ - pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG; - - /* - * Ensure we substitute the videobuf-dma-contig version of the - * mmap_mapper() callback with our own wrapper, used for switching - * automatically to videobuf-dma-sg on buffer allocation failure. - */ - if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) { - pcdev->mmap_mapper = q->int_ops->mmap_mapper; - q->int_ops->mmap_mapper = omap1_cam_mmap_mapper; - } -} - -static int omap1_cam_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - int i; - - /* - * This is for locking debugging only. I removed spinlocks and now I - * check whether .prepare is ever called on a linked buffer, or whether - * a dma IRQ can occur for an in-work or unlinked buffer. Until now - * it hadn't triggered - */ - for (i = 0; i < p->count; i++) { - struct omap1_cam_buf *buf = container_of(icd->vb_vidq.bufs[i], - struct omap1_cam_buf, vb); - buf->inwork = 0; - INIT_LIST_HEAD(&buf->vb.queue); - } - - return 0; -} - -static int omap1_cam_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the friendly caller:-> */ - strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int omap1_cam_set_bus_param(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - u32 pixfmt = icd->current_fmt->host_fmt->fourcc; - const struct soc_camera_format_xlate *xlate; - const struct soc_mbus_pixelfmt *fmt; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long common_flags; - u32 ctrlclock, mode; - int ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS); - if (!common_flags) { - dev_warn(dev, - "Flags incompatible: camera 0x%x, host 0x%x\n", - cfg.flags, SOCAM_BUS_FLAGS); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = SOCAM_BUS_FLAGS; - } - - /* Make choices, possibly based on platform configuration */ - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (!pcdev->pdata || - pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); - return ret; - } - - ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - if (ctrlclock & LCLK_EN) - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) { - dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n"); - ctrlclock |= POLCLK; - } else { - dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n"); - ctrlclock &= ~POLCLK; - } - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - - if (ctrlclock & LCLK_EN) - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - /* select bus endianness */ - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - fmt = xlate->host_fmt; - - mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA); - if (fmt->order == SOC_MBUS_ORDER_LE) { - dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n"); - CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD); - } else { - dev_dbg(dev, "MODE_REG |= ORDERCAMD\n"); - CAM_WRITE(pcdev, MODE, mode | ORDERCAMD); - } - - return 0; -} - -static unsigned int omap1_cam_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - struct omap1_cam_buf *buf; - - buf = list_entry(icd->vb_vidq.stream.next, struct omap1_cam_buf, - vb.stream); - - poll_wait(file, &buf->vb.done, pt); - - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - - return 0; -} - -static struct soc_camera_host_ops omap1_host_ops = { - .owner = THIS_MODULE, - .add = omap1_cam_add_device, - .remove = omap1_cam_remove_device, - .clock_start = omap1_cam_clock_start, - .clock_stop = omap1_cam_clock_stop, - .get_formats = omap1_cam_get_formats, - .set_crop = omap1_cam_set_crop, - .set_fmt = omap1_cam_set_fmt, - .try_fmt = omap1_cam_try_fmt, - .init_videobuf = omap1_cam_init_videobuf, - .reqbufs = omap1_cam_reqbufs, - .querycap = omap1_cam_querycap, - .set_bus_param = omap1_cam_set_bus_param, - .poll = omap1_cam_poll, -}; - -static int omap1_cam_probe(struct platform_device *pdev) -{ - struct omap1_cam_dev *pcdev; - struct resource *res; - struct clk *clk; - void __iomem *base; - unsigned int irq; - int err = 0; - - irq = platform_get_irq(pdev, 0); - if ((int)irq <= 0) { - err = -ENODEV; - goto exit; - } - - clk = devm_clk_get(&pdev->dev, "armper_ck"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev) + resource_size(res), - GFP_KERNEL); - if (!pcdev) - return -ENOMEM; - - pcdev->clk = clk; - - pcdev->pdata = pdev->dev.platform_data; - if (pcdev->pdata) { - pcdev->pflags = pcdev->pdata->flags; - pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000; - } - - switch (pcdev->camexclk) { - case 6000000: - case 8000000: - case 9600000: - case 12000000: - case 24000000: - break; - default: - /* pcdev->camexclk != 0 => pcdev->pdata != NULL */ - dev_warn(&pdev->dev, - "Incorrect sensor clock frequency %ld kHz, " - "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, " - "please correct your platform data\n", - pcdev->pdata->camexclk_khz); - pcdev->camexclk = 0; - case 0: - dev_info(&pdev->dev, "Not providing sensor clock\n"); - } - - INIT_LIST_HEAD(&pcdev->capture); - spin_lock_init(&pcdev->lock); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - pcdev->irq = irq; - pcdev->base = base; - - sensor_reset(pcdev, true); - - err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME, - dma_isr, (void *)pcdev, &pcdev->dma_ch); - if (err < 0) { - dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n"); - return -EBUSY; - } - dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch); - - /* preconfigure DMA */ - omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB, - OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA, - 0, 0); - omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4); - /* setup DMA autoinitialization */ - omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch); - - err = devm_request_irq(&pdev->dev, pcdev->irq, cam_isr, 0, DRIVER_NAME, - pcdev); - if (err) { - dev_err(&pdev->dev, "Camera interrupt register failed\n"); - goto exit_free_dma; - } - - pcdev->soc_host.drv_name = DRIVER_NAME; - pcdev->soc_host.ops = &omap1_host_ops; - pcdev->soc_host.priv = pcdev; - pcdev->soc_host.v4l2_dev.dev = &pdev->dev; - pcdev->soc_host.nr = pdev->id; - - err = soc_camera_host_register(&pcdev->soc_host); - if (err) - return err; - - dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n"); - - return 0; - -exit_free_dma: - omap_free_dma(pcdev->dma_ch); -exit: - return err; -} - -static int omap1_cam_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct omap1_cam_dev *pcdev = container_of(soc_host, - struct omap1_cam_dev, soc_host); - - omap_free_dma(pcdev->dma_ch); - - soc_camera_host_unregister(soc_host); - - dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n"); - - return 0; -} - -static struct platform_driver omap1_cam_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = omap1_cam_probe, - .remove = omap1_cam_remove, -}; - -module_platform_driver(omap1_cam_driver); - -module_param(sg_mode, bool, 0644); -MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg"); - -MODULE_DESCRIPTION("OMAP1 Camera Interface driver"); -MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(DRIVER_VERSION); -MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/staging/media/timb/Kconfig b/drivers/staging/media/timb/Kconfig deleted file mode 100644 index e413fecc1e67..000000000000 --- a/drivers/staging/media/timb/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -config VIDEO_TIMBERDALE - tristate "Support for timberdale Video In/LogiWIN" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && HAS_DMA - depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST - select VIDEO_ADV7180 - select VIDEOBUF_DMA_CONTIG - ---help--- - Add support for the Video In peripherial of the timberdale FPGA. - - This driver is deprecated and will be removed soon unless someone - will start the work to convert this driver to the vb2 framework. diff --git a/drivers/staging/media/timb/Makefile b/drivers/staging/media/timb/Makefile deleted file mode 100644 index 4c989c23a0e0..000000000000 --- a/drivers/staging/media/timb/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o diff --git a/drivers/staging/media/timb/timblogiw.c b/drivers/staging/media/timb/timblogiw.c deleted file mode 100644 index 113c9f3c0b3e..000000000000 --- a/drivers/staging/media/timb/timblogiw.c +++ /dev/null @@ -1,870 +0,0 @@ -/* - * timblogiw.c timberdale FPGA LogiWin Video In driver - * Copyright (c) 2009-2010 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Timberdale FPGA LogiWin Video In - */ - -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/dmaengine.h> -#include <linux/scatterlist.h> -#include <linux/interrupt.h> -#include <linux/list.h> -#include <linux/i2c.h> -#include <linux/module.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-device.h> -#include <media/videobuf-dma-contig.h> -#include <linux/platform_data/media/timb_video.h> - -#define DRIVER_NAME "timb-video" - -#define TIMBLOGIWIN_NAME "Timberdale Video-In" -#define TIMBLOGIW_VERSION_CODE 0x04 - -#define TIMBLOGIW_LINES_PER_DESC 44 -#define TIMBLOGIW_MAX_VIDEO_MEM 16 - -#define TIMBLOGIW_HAS_DECODER(lw) (lw->pdata.encoder.module_name) - - -struct timblogiw { - struct video_device video_dev; - struct v4l2_device v4l2_dev; /* mutual exclusion */ - struct mutex lock; - struct device *dev; - struct timb_video_platform_data pdata; - struct v4l2_subdev *sd_enc; /* encoder */ - bool opened; -}; - -struct timblogiw_tvnorm { - v4l2_std_id std; - u16 width; - u16 height; - u8 fps; -}; - -struct timblogiw_fh { - struct videobuf_queue vb_vidq; - struct timblogiw_tvnorm const *cur_norm; - struct list_head capture; - struct dma_chan *chan; - spinlock_t queue_lock; /* mutual exclusion */ - unsigned int frame_count; -}; - -struct timblogiw_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - struct scatterlist sg[16]; - dma_cookie_t cookie; - struct timblogiw_fh *fh; -}; - -static const struct timblogiw_tvnorm timblogiw_tvnorms[] = { - { - .std = V4L2_STD_PAL, - .width = 720, - .height = 576, - .fps = 25 - }, - { - .std = V4L2_STD_NTSC, - .width = 720, - .height = 480, - .fps = 30 - } -}; - -static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm) -{ - return norm->width * 2; -} - - -static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm) -{ - return norm->height * timblogiw_bytes_per_line(norm); -} - -static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std) -{ - int i; - for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++) - if (timblogiw_tvnorms[i].std & std) - return timblogiw_tvnorms + i; - - /* default to first element */ - return timblogiw_tvnorms; -} - -static void timblogiw_dma_cb(void *data) -{ - struct timblogiw_buffer *buf = data; - struct timblogiw_fh *fh = buf->fh; - struct videobuf_buffer *vb = &buf->vb; - - spin_lock(&fh->queue_lock); - - /* mark the transfer done */ - buf->cookie = -1; - - fh->frame_count++; - - if (vb->state != VIDEOBUF_ERROR) { - list_del(&vb->queue); - v4l2_get_timestamp(&vb->ts); - vb->field_count = fh->frame_count * 2; - vb->state = VIDEOBUF_DONE; - - wake_up(&vb->done); - } - - if (!list_empty(&fh->capture)) { - vb = list_entry(fh->capture.next, struct videobuf_buffer, - queue); - vb->state = VIDEOBUF_ACTIVE; - } - - spin_unlock(&fh->queue_lock); -} - -static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param) -{ - return chan->chan_id == (uintptr_t)filter_param; -} - -/* IOCTL functions */ - -static int timblogiw_g_fmt(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s entry\n", __func__); - - if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - mutex_lock(&lw->lock); - - format->fmt.pix.width = fh->cur_norm->width; - format->fmt.pix.height = fh->cur_norm->height; - format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; - format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm); - format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm); - format->fmt.pix.field = V4L2_FIELD_NONE; - - mutex_unlock(&lw->lock); - - return 0; -} - -static int timblogiw_try_fmt(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct video_device *vdev = video_devdata(file); - struct v4l2_pix_format *pix = &format->fmt.pix; - - dev_dbg(&vdev->dev, - "%s - width=%d, height=%d, pixelformat=%d, field=%d\n" - "bytes per line %d, size image: %d, colorspace: %d\n", - __func__, - pix->width, pix->height, pix->pixelformat, pix->field, - pix->bytesperline, pix->sizeimage, pix->colorspace); - - if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (pix->field != V4L2_FIELD_NONE) - return -EINVAL; - - if (pix->pixelformat != V4L2_PIX_FMT_UYVY) - return -EINVAL; - - return 0; -} - -static int timblogiw_s_fmt(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - struct v4l2_pix_format *pix = &format->fmt.pix; - int err; - - mutex_lock(&lw->lock); - - err = timblogiw_try_fmt(file, priv, format); - if (err) - goto out; - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - dev_err(&vdev->dev, "%s queue busy\n", __func__); - err = -EBUSY; - goto out; - } - - pix->width = fh->cur_norm->width; - pix->height = fh->cur_norm->height; - -out: - mutex_unlock(&lw->lock); - return err; -} - -static int timblogiw_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1); - strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->name); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int timblogiw_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *fmt) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s, index: %d\n", __func__, fmt->index); - - if (fmt->index != 0) - return -EINVAL; - memset(fmt, 0, sizeof(*fmt)); - fmt->index = 0; - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strncpy(fmt->description, "4:2:2, packed, YUYV", - sizeof(fmt->description)-1); - fmt->pixelformat = V4L2_PIX_FMT_UYVY; - - return 0; -} - -static int timblogiw_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct timblogiw_fh *fh = priv; - struct v4l2_captureparm *cp = &sp->parm.capture; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = fh->cur_norm->fps; - - return 0; -} - -static int timblogiw_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_reqbufs(&fh->vb_vidq, rb); -} - -static int timblogiw_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_querybuf(&fh->vb_vidq, b); -} - -static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_qbuf(&fh->vb_vidq, b); -} - -static int timblogiw_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); -} - -static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - *std = fh->cur_norm->std; - return 0; -} - -static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - int err = 0; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - mutex_lock(&lw->lock); - - if (TIMBLOGIW_HAS_DECODER(lw)) - err = v4l2_subdev_call(lw->sd_enc, video, s_std, std); - - if (!err) - fh->cur_norm = timblogiw_get_norm(std); - - mutex_unlock(&lw->lock); - - return err; -} - -static int timblogiw_enuminput(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct video_device *vdev = video_devdata(file); - int i; - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - if (inp->index != 0) - return -EINVAL; - - inp->index = 0; - - strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1); - inp->type = V4L2_INPUT_TYPE_CAMERA; - - inp->std = 0; - for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++) - inp->std |= timblogiw_tvnorms[i].std; - - return 0; -} - -static int timblogiw_g_input(struct file *file, void *priv, - unsigned int *input) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - *input = 0; - - return 0; -} - -static int timblogiw_s_input(struct file *file, void *priv, unsigned int input) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - if (input != 0) - return -EINVAL; - return 0; -} - -static int timblogiw_streamon(struct file *file, void *priv, enum v4l2_buf_type type) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_dbg(&vdev->dev, "%s - No capture device\n", __func__); - return -EINVAL; - } - - fh->frame_count = 0; - return videobuf_streamon(&fh->vb_vidq); -} - -static int timblogiw_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s entry\n", __func__); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamoff(&fh->vb_vidq); -} - -static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s entry\n", __func__); - - if (TIMBLOGIW_HAS_DECODER(lw)) - return v4l2_subdev_call(lw->sd_enc, video, querystd, std); - else { - *std = fh->cur_norm->std; - return 0; - } -} - -static int timblogiw_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n", __func__, - fsize->index, fsize->pixel_format); - - if ((fsize->index != 0) || - (fsize->pixel_format != V4L2_PIX_FMT_UYVY)) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fh->cur_norm->width; - fsize->discrete.height = fh->cur_norm->height; - - return 0; -} - -/* Video buffer functions */ - -static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct timblogiw_fh *fh = vq->priv_data; - - *size = timblogiw_frame_size(fh->cur_norm); - - if (!*count) - *count = 32; - - while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024) - (*count)--; - - return 0; -} - -static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct timblogiw_fh *fh = vq->priv_data; - struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, - vb); - unsigned int data_size = timblogiw_frame_size(fh->cur_norm); - int err = 0; - - if (vb->baddr && vb->bsize < data_size) - /* User provided buffer, but it is too small */ - return -ENOMEM; - - vb->size = data_size; - vb->width = fh->cur_norm->width; - vb->height = fh->cur_norm->height; - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int i; - unsigned int size; - unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC * - timblogiw_bytes_per_line(fh->cur_norm); - dma_addr_t addr; - - sg_init_table(buf->sg, ARRAY_SIZE(buf->sg)); - - err = videobuf_iolock(vq, vb, NULL); - if (err) - goto err; - - addr = videobuf_to_dma_contig(vb); - for (i = 0, size = 0; size < data_size; i++) { - sg_dma_address(buf->sg + i) = addr + size; - size += bytes_per_desc; - sg_dma_len(buf->sg + i) = (size > data_size) ? - (bytes_per_desc - (size - data_size)) : - bytes_per_desc; - } - - vb->state = VIDEOBUF_PREPARED; - buf->cookie = -1; - buf->fh = fh; - } - - return 0; - -err: - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; - return err; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct timblogiw_fh *fh = vq->priv_data; - struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, - vb); - struct dma_async_tx_descriptor *desc; - int sg_elems; - int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC * - timblogiw_bytes_per_line(fh->cur_norm); - - sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc; - sg_elems += - (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0; - - if (list_empty(&fh->capture)) - vb->state = VIDEOBUF_ACTIVE; - else - vb->state = VIDEOBUF_QUEUED; - - list_add_tail(&vb->queue, &fh->capture); - - spin_unlock_irq(&fh->queue_lock); - - desc = dmaengine_prep_slave_sg(fh->chan, - buf->sg, sg_elems, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); - if (!desc) { - spin_lock_irq(&fh->queue_lock); - list_del_init(&vb->queue); - vb->state = VIDEOBUF_PREPARED; - return; - } - - desc->callback_param = buf; - desc->callback = timblogiw_dma_cb; - - buf->cookie = desc->tx_submit(desc); - - spin_lock_irq(&fh->queue_lock); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct timblogiw_fh *fh = vq->priv_data; - struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, - vb); - - videobuf_waiton(vq, vb, 0, 0); - if (buf->cookie >= 0) - dma_sync_wait(fh->chan, buf->cookie); - - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static struct videobuf_queue_ops timblogiw_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/* Device Operations functions */ - -static int timblogiw_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh; - v4l2_std_id std; - dma_cap_mask_t mask; - int err = 0; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - mutex_lock(&lw->lock); - if (lw->opened) { - err = -EBUSY; - goto out; - } - - if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) { - struct i2c_adapter *adapt; - - /* find the video decoder */ - adapt = i2c_get_adapter(lw->pdata.i2c_adapter); - if (!adapt) { - dev_err(&vdev->dev, "No I2C bus #%d\n", - lw->pdata.i2c_adapter); - err = -ENODEV; - goto out; - } - - /* now find the encoder */ - lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt, - lw->pdata.encoder.info, NULL); - - i2c_put_adapter(adapt); - - if (!lw->sd_enc) { - dev_err(&vdev->dev, "Failed to get encoder: %s\n", - lw->pdata.encoder.module_name); - err = -ENODEV; - goto out; - } - } - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) { - err = -ENOMEM; - goto out; - } - - fh->cur_norm = timblogiw_tvnorms; - timblogiw_querystd(file, fh, &std); - fh->cur_norm = timblogiw_get_norm(std); - - INIT_LIST_HEAD(&fh->capture); - spin_lock_init(&fh->queue_lock); - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_PRIVATE, mask); - - /* find the DMA channel */ - fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn, - (void *)(uintptr_t)lw->pdata.dma_channel); - if (!fh->chan) { - dev_err(&vdev->dev, "Failed to get DMA channel\n"); - kfree(fh); - err = -ENODEV; - goto out; - } - - file->private_data = fh; - videobuf_queue_dma_contig_init(&fh->vb_vidq, - &timblogiw_video_qops, lw->dev, &fh->queue_lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct timblogiw_buffer), fh, NULL); - - lw->opened = true; -out: - mutex_unlock(&lw->lock); - - return err; -} - -static int timblogiw_close(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - - dma_release_channel(fh->chan); - - kfree(fh); - - mutex_lock(&lw->lock); - lw->opened = false; - mutex_unlock(&lw->lock); - return 0; -} - -static ssize_t timblogiw_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static unsigned int timblogiw_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_poll_stream(file, &fh->vb_vidq, wait); -} - -static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_mmap_mapper(&fh->vb_vidq, vma); -} - -/* Platform device functions */ - -static struct v4l2_ioctl_ops timblogiw_ioctl_ops = { - .vidioc_querycap = timblogiw_querycap, - .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt, - .vidioc_g_fmt_vid_cap = timblogiw_g_fmt, - .vidioc_try_fmt_vid_cap = timblogiw_try_fmt, - .vidioc_s_fmt_vid_cap = timblogiw_s_fmt, - .vidioc_g_parm = timblogiw_g_parm, - .vidioc_reqbufs = timblogiw_reqbufs, - .vidioc_querybuf = timblogiw_querybuf, - .vidioc_qbuf = timblogiw_qbuf, - .vidioc_dqbuf = timblogiw_dqbuf, - .vidioc_g_std = timblogiw_g_std, - .vidioc_s_std = timblogiw_s_std, - .vidioc_enum_input = timblogiw_enuminput, - .vidioc_g_input = timblogiw_g_input, - .vidioc_s_input = timblogiw_s_input, - .vidioc_streamon = timblogiw_streamon, - .vidioc_streamoff = timblogiw_streamoff, - .vidioc_querystd = timblogiw_querystd, - .vidioc_enum_framesizes = timblogiw_enum_framesizes, -}; - -static struct v4l2_file_operations timblogiw_fops = { - .owner = THIS_MODULE, - .open = timblogiw_open, - .release = timblogiw_close, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = timblogiw_mmap, - .read = timblogiw_read, - .poll = timblogiw_poll, -}; - -static struct video_device timblogiw_template = { - .name = TIMBLOGIWIN_NAME, - .fops = &timblogiw_fops, - .ioctl_ops = &timblogiw_ioctl_ops, - .release = video_device_release_empty, - .minor = -1, - .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC -}; - -static int timblogiw_probe(struct platform_device *pdev) -{ - int err; - struct timblogiw *lw = NULL; - struct timb_video_platform_data *pdata = pdev->dev.platform_data; - - if (!pdata) { - dev_err(&pdev->dev, "No platform data\n"); - err = -EINVAL; - goto err; - } - - if (!pdata->encoder.module_name) - dev_info(&pdev->dev, "Running without decoder\n"); - - lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL); - if (!lw) { - err = -ENOMEM; - goto err; - } - - if (pdev->dev.parent) - lw->dev = pdev->dev.parent; - else - lw->dev = &pdev->dev; - - memcpy(&lw->pdata, pdata, sizeof(lw->pdata)); - - mutex_init(&lw->lock); - - lw->video_dev = timblogiw_template; - - strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name)); - err = v4l2_device_register(NULL, &lw->v4l2_dev); - if (err) - goto err; - - lw->video_dev.v4l2_dev = &lw->v4l2_dev; - - platform_set_drvdata(pdev, lw); - video_set_drvdata(&lw->video_dev, lw); - - err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0); - if (err) { - dev_err(&pdev->dev, "Error reg video: %d\n", err); - goto err_request; - } - - return 0; - -err_request: - v4l2_device_unregister(&lw->v4l2_dev); -err: - dev_err(&pdev->dev, "Failed to register: %d\n", err); - - return err; -} - -static int timblogiw_remove(struct platform_device *pdev) -{ - struct timblogiw *lw = platform_get_drvdata(pdev); - - video_unregister_device(&lw->video_dev); - - v4l2_device_unregister(&lw->v4l2_dev); - - return 0; -} - -static struct platform_driver timblogiw_platform_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = timblogiw_probe, - .remove = timblogiw_remove, -}; - -module_platform_driver(timblogiw_platform_driver); - -MODULE_DESCRIPTION(TIMBLOGIWIN_NAME); -MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:"DRIVER_NAME); diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index c201060e0c6d..f8e1992d6423 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -1,7 +1,8 @@ #ifndef __OF_RESERVED_MEM_H #define __OF_RESERVED_MEM_H -struct device; +#include <linux/device.h> + struct of_phandle_args; struct reserved_mem_ops; @@ -28,7 +29,9 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem); _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn) #ifdef CONFIG_OF_RESERVED_MEM -int of_reserved_mem_device_init(struct device *dev); + +int of_reserved_mem_device_init_by_idx(struct device *dev, + struct device_node *np, int idx); void of_reserved_mem_device_release(struct device *dev); int early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, @@ -42,7 +45,8 @@ void fdt_init_reserved_mem(void); void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size); #else -static inline int of_reserved_mem_device_init(struct device *dev) +static inline int of_reserved_mem_device_init_by_idx(struct device *dev, + struct device_node *np, int idx) { return -ENOSYS; } @@ -53,4 +57,19 @@ static inline void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size) { } #endif +/** + * of_reserved_mem_device_init() - assign reserved memory region to given device + * @dev: Pointer to the device to configure + * + * This function assigns respective DMA-mapping operations based on the first + * reserved memory region specified by 'memory-region' property in device tree + * node of the given device. + * + * Returns error code or zero on success. + */ +static inline int of_reserved_mem_device_init(struct device *dev) +{ + return of_reserved_mem_device_init_by_idx(dev, dev->of_node, 0); +} + #endif /* __OF_RESERVED_MEM_H */ diff --git a/include/media/media-device.h b/include/media/media-device.h index a9b33c47310d..f743ae2210ee 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -347,7 +347,7 @@ struct media_entity_notify { struct media_device { /* dev->driver_data points to this struct. */ struct device *dev; - struct media_devnode devnode; + struct media_devnode *devnode; char model[32]; char driver_name[32]; @@ -393,9 +393,6 @@ struct usb_device; #define MEDIA_DEV_NOTIFY_PRE_LINK_CH 0 #define MEDIA_DEV_NOTIFY_POST_LINK_CH 1 -/* media_devnode to media_device */ -#define to_media_device(node) container_of(node, struct media_device, devnode) - /** * media_entity_enum_init - Initialise an entity enumeration * diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index fe42f08e72bd..37d494805944 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -33,6 +33,8 @@ #include <linux/device.h> #include <linux/cdev.h> +struct media_device; + /* * Flag to mark the media_devnode struct as registered. Drivers must not touch * this flag directly, it will be set and cleared by media_devnode_register and @@ -67,8 +69,9 @@ struct media_file_operations { /** * struct media_devnode - Media device node + * @media_dev: pointer to struct &media_device * @fops: pointer to struct &media_file_operations with media device ops - * @dev: struct device pointer for the media controller device + * @dev: pointer to struct &device containing the media controller device * @cdev: struct cdev pointer character device * @parent: parent device * @minor: device node minor number @@ -81,6 +84,8 @@ struct media_file_operations { * before registering the node. */ struct media_devnode { + struct media_device *media_dev; + /* device ops */ const struct media_file_operations *fops; @@ -94,7 +99,7 @@ struct media_devnode { unsigned long flags; /* Use bitops to access flags */ /* callbacks */ - void (*release)(struct media_devnode *mdev); + void (*release)(struct media_devnode *devnode); }; /* dev to media_devnode */ @@ -103,7 +108,8 @@ struct media_devnode { /** * media_devnode_register - register a media device node * - * @mdev: media device node structure we want to register + * @mdev: struct media_device we want to register a device node + * @devnode: media device node structure we want to register * @owner: should be filled with %THIS_MODULE * * The registration code assigns minor numbers and registers the new device node @@ -116,20 +122,33 @@ struct media_devnode { * the media_devnode structure is *not* called, so the caller is responsible for * freeing any data. */ -int __must_check media_devnode_register(struct media_devnode *mdev, +int __must_check media_devnode_register(struct media_device *mdev, + struct media_devnode *devnode, struct module *owner); /** + * media_devnode_unregister_prepare - clear the media device node register bit + * @devnode: the device node to prepare for unregister + * + * This clears the passed device register bit. Future open calls will be met + * with errors. Should be called before media_devnode_unregister() to avoid + * races with unregister and device file open calls. + * + * This function can safely be called if the device node has never been + * registered or has already been unregistered. + */ +void media_devnode_unregister_prepare(struct media_devnode *devnode); + +/** * media_devnode_unregister - unregister a media device node - * @mdev: the device node to unregister + * @devnode: the device node to unregister * * This unregisters the passed device. Future open calls will be met with * errors. * - * This function can safely be called if the device node has never been - * registered or has already been unregistered. + * Should be called after media_devnode_unregister_prepare() */ -void media_devnode_unregister(struct media_devnode *mdev); +void media_devnode_unregister(struct media_devnode *devnode); /** * media_devnode_data - returns a pointer to the &media_devnode @@ -145,11 +164,16 @@ static inline struct media_devnode *media_devnode_data(struct file *filp) * media_devnode_is_registered - returns true if &media_devnode is registered; * false otherwise. * - * @mdev: pointer to struct &media_devnode. + * @devnode: pointer to struct &media_devnode. + * + * Note: If mdev is NULL, it also returns false. */ -static inline int media_devnode_is_registered(struct media_devnode *mdev) +static inline int media_devnode_is_registered(struct media_devnode *devnode) { - return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + if (!devnode) + return false; + + return test_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); } #endif /* _MEDIA_DEVNODE_H */ diff --git a/include/media/rcar-fcp.h b/include/media/rcar-fcp.h new file mode 100644 index 000000000000..4c7fc77eaf29 --- /dev/null +++ b/include/media/rcar-fcp.h @@ -0,0 +1,37 @@ +/* + * rcar-fcp.h -- R-Car Frame Compression Processor Driver + * + * Copyright (C) 2016 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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. + */ +#ifndef __MEDIA_RCAR_FCP_H__ +#define __MEDIA_RCAR_FCP_H__ + +struct device_node; +struct rcar_fcp_device; + +#if IS_ENABLED(CONFIG_VIDEO_RENESAS_FCP) +struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np); +void rcar_fcp_put(struct rcar_fcp_device *fcp); +int rcar_fcp_enable(struct rcar_fcp_device *fcp); +void rcar_fcp_disable(struct rcar_fcp_device *fcp); +#else +static inline struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np) +{ + return ERR_PTR(-ENOENT); +} +static inline void rcar_fcp_put(struct rcar_fcp_device *fcp) { } +static inline int rcar_fcp_enable(struct rcar_fcp_device *fcp) +{ + return -ENOSYS; +} +static inline void rcar_fcp_disable(struct rcar_fcp_device *fcp) { } +#endif + +#endif /* __MEDIA_RCAR_FCP_H__ */ diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h index 2087c9a68be3..f7dc8401817e 100644 --- a/include/media/videobuf2-dma-contig.h +++ b/include/media/videobuf2-dma-contig.h @@ -35,6 +35,8 @@ static inline void *vb2_dma_contig_init_ctx(struct device *dev) } void vb2_dma_contig_cleanup_ctx(void *alloc_ctx); +int vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size); +void vb2_dma_contig_clear_max_seg_size(struct device *dev); extern const struct vb2_mem_ops vb2_dma_contig_memops; diff --git a/include/media/vsp1.h b/include/media/vsp1.h index 3e654a0455bd..9322d9775fb7 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -14,31 +14,28 @@ #define __MEDIA_VSP1_H__ #include <linux/types.h> +#include <linux/videodev2.h> struct device; -struct v4l2_rect; int vsp1_du_init(struct device *dev); int vsp1_du_setup_lif(struct device *dev, unsigned int width, unsigned int height); +struct vsp1_du_atomic_config { + u32 pixelformat; + unsigned int pitch; + dma_addr_t mem[2]; + struct v4l2_rect src; + struct v4l2_rect dst; + unsigned int alpha; + unsigned int zpos; +}; + void vsp1_du_atomic_begin(struct device *dev); -int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf, - u32 pixelformat, unsigned int pitch, - dma_addr_t mem[2], const struct v4l2_rect *src, - const struct v4l2_rect *dst, unsigned int alpha, - unsigned int zpos); +int vsp1_du_atomic_update(struct device *dev, unsigned int rpf, + const struct vsp1_du_atomic_config *cfg); void vsp1_du_atomic_flush(struct device *dev); -static inline int vsp1_du_atomic_update(struct device *dev, - unsigned int rpf_index, u32 pixelformat, - unsigned int pitch, dma_addr_t mem[2], - const struct v4l2_rect *src, - const struct v4l2_rect *dst) -{ - return vsp1_du_atomic_update_ext(dev, rpf_index, pixelformat, pitch, - mem, src, dst, 255, 0); -} - #endif /* __MEDIA_VSP1_H__ */ diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index df59edee25d1..7acf0f634f70 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -95,6 +95,16 @@ struct media_device_info { #define MEDIA_ENT_F_AUDIO_MIXER (MEDIA_ENT_F_BASE + 0x03003) /* + * Processing entities + */ +#define MEDIA_ENT_F_PROC_VIDEO_COMPOSER (MEDIA_ENT_F_BASE + 0x4001) +#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER (MEDIA_ENT_F_BASE + 0x4002) +#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV (MEDIA_ENT_F_BASE + 0x4003) +#define MEDIA_ENT_F_PROC_VIDEO_LUT (MEDIA_ENT_F_BASE + 0x4004) +#define MEDIA_ENT_F_PROC_VIDEO_SCALER (MEDIA_ENT_F_BASE + 0x4005) +#define MEDIA_ENT_F_PROC_VIDEO_STATISTICS (MEDIA_ENT_F_BASE + 0x4006) + +/* * Connectors */ /* It is a responsibility of the entity drivers to add connectors and links */ diff --git a/include/uapi/linux/vsp1.h b/include/uapi/linux/vsp1.h deleted file mode 100644 index 9a823696d816..000000000000 --- a/include/uapi/linux/vsp1.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * vsp1.h - * - * Renesas R-Car VSP1 - User-space API - * - * Copyright (C) 2013 Renesas Corporation - * - * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __VSP1_USER_H__ -#define __VSP1_USER_H__ - -#include <linux/types.h> -#include <linux/videodev2.h> - -/* - * Private IOCTLs - * - * VIDIOC_VSP1_LUT_CONFIG - Configure the lookup table - */ - -#define VIDIOC_VSP1_LUT_CONFIG \ - _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct vsp1_lut_config) - -struct vsp1_lut_config { - __u32 lut[256]; -}; - -#endif /* __VSP1_USER_H__ */ |