Merge remote-tracking branch 'media_tree/vsp1' into generic-zpos-v8

This commit is contained in:
Benjamin Gaignard 2016-07-29 09:38:55 +02:00
commit 62c2cd0f49
122 changed files with 7444 additions and 7070 deletions

View File

@ -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>

View File

@ -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";
};

View File

@ -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>;
};

View File

@ -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

View File

@ -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>;
};

View File

@ -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>

View File

@ -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;

View File

@ -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 *

View File

@ -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;
}

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -26,6 +26,7 @@
#define I2C_SLVT 1
#define CXD2841ER_CHIP_ID 0xa7
#define CXD2854ER_CHIP_ID 0xc1
#define CXD2841ER_DVBS_POLLING_INVL 10

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -19,26 +19,88 @@
static int mn88472_get_tune_settings(struct dvb_frontend *fe,
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 {
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]);
/* 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;
ret = regmap_write(dev->regmap[2], 0x32, c->stream_id);
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);
if (ret)
goto err;
break;
default:
ret = -EINVAL;
goto err;
break;
}
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);
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;
ret = regmap_write(dev->regmap[2], 0x0c, 0x00);
if (ret)
goto err;
/* check if firmware is already running */
ret = regmap_read(dev->regmap[0], 0xf5, &tmp);
/* Check if firmware is already running */
ret = regmap_read(dev->regmap[0], 0xf5, &utmp);
if (ret)
goto err;
if (!(utmp & 0x01))
goto warm;
if (!(tmp & 0x1)) {
dev_info(&client->dev, "firmware already running\n");
dev->warm = true;
return 0;
}
/* 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;
/* 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;
}
if (tmp & 0x10) {
dev_err(&client->dev,
"firmware parity check failed=0x%x\n", tmp);
goto firmware_release;
}
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(fw);
fw = NULL;
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;
/* warm state */
dev->warm = true;
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;
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 */
ret = regmap_write(dev->regmap[2], 0x0b, 0x30);
/* 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)
{
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], &regmap_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:
@ -562,6 +598,7 @@ MODULE_DEVICE_TABLE(i2c, mn88472_id_table);
static struct i2c_driver mn88472_driver = {
.driver = {
.name = "mn88472",
.suppress_bind_attrs = true,
},
.probe = mn88472_probe,
.remove = mn88472_remove,

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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,6 +970,9 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
else
u8tmp = 0x00;
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,6 +1008,10 @@ 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;
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;
@ -1010,6 +1019,10 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
/* add PID */
buf[0] = (pid >> 8) & 0xff;
buf[1] = (pid >> 0) & 0xff;
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;

View File

@ -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 {

View File

@ -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)
return ret;
ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
ret = media_devnode_register(mdev, devnode, owner);
if (ret < 0) {
media_devnode_unregister(&mdev->devnode);
/* devnode free is handled in media_devnode_*() */
mdev->devnode = NULL;
return ret;
}
ret = device_create_file(&devnode->dev, &dev_attr_model);
if (ret < 0) {
/* 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);

View File

@ -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);
device_unregister(&mdev->dev);
}
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_del(&devnode->dev);
devnode->media_dev = NULL;
put_device(&devnode->dev);
}
/*

View File

@ -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.

View File

@ -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);

View File

@ -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;
}

View File

@ -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,85 +402,97 @@ 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,
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__);
}
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;
}
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,
}
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;
}
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;
}
if (ndev->rev == NETUP_HW_REV_1_3) {
horus3a_conf.set_tuner_priv = &ndev->dma[num];
if (!dvb_attach(horus3a_attach, fe0->dvb.frontend,
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 DVB-S/S2 tuner frontend\n",
"%s(): unable to attach HORUS3A DVB-S/S2 tuner frontend\n",
__func__);
goto frontend_detach;
}
if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend,
} 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, 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) {
if (fes[1]->dvb.frontend == NULL) {
dev_dbg(&ndev->pci_dev->dev,
"%s(): unable to attach DVB-T frontend\n", __func__);
"%s(): unable to attach Ter frontend\n", __func__);
goto frontend_detach;
}
fe1->dvb.frontend->id = 1;
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, fe1->dvb.frontend,
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 DVB-T tuner frontend\n",
"%s(): unable to attach Ter 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) {
dev_dbg(&ndev->pci_dev->dev,
"%s(): unable to attach DVB-C 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",
} 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],
THIS_MODULE, NULL,
@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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");

View File

@ -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.

View File

@ -0,0 +1,3 @@
rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o
obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin.o

View File

@ -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");

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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))
of_reserved_mem_device_release(dev);
}
static struct device *s5p_mfc_alloc_memdev(struct device *dev,
const char *name, unsigned int idx)
{
struct device *child;
int ret;
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;
}
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;
}
/*
* 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;
return !strcmp(dev_name(dev), (char *)data);
}
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);
static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev)
{
unsigned int mem_info[2] = { };
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;
}
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;
}
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;
}
return 0;
}
/* 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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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);
}

View File

@ -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.

View File

@ -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,6 +203,7 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1)
}
v4l2_device_unregister(&vsp1->v4l2_dev);
if (vsp1->info->uapi)
media_device_unregister(&vsp1->media_dev);
media_device_cleanup(&vsp1->media_dev);
@ -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;
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;
int ret;
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++;
mutex_unlock(&vsp1->lock);
return ret;
ret = pm_runtime_get_sync(vsp1->dev);
return ret < 0 ? ret : 0;
}
/*
@ -501,12 +497,7 @@ int vsp1_device_get(struct vsp1_device *vsp1)
*/
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));
if (vsp1->ref_count == 0)
return 0;
clk_prepare_enable(vsp1->clock);
pm_runtime_force_resume(vsp1->dev);
vsp1_pipelines_resume(vsp1);
return 0;
}
#endif
static int vsp1_pm_runtime_suspend(struct device *dev)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
rcar_fcp_disable(vsp1->fcp);
return 0;
}
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;
}

View File

@ -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,
vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad)
unsigned int pad, unsigned int target)
{
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;

View File

@ -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,
vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad);
unsigned int pad, unsigned int target);
int vsp1_entity_init_cfg(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg);

View File

@ -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);

View File

@ -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);

View File

@ -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);
struct vsp1_lut *lut =
container_of(ctrl->handler, struct vsp1_lut, ctrls);
switch (cmd) {
case VIDIOC_VSP1_LUT_CONFIG:
return lut_set_table(lut, arg);
switch (ctrl->id) {
case V4L2_CID_VSP1_LUT_TABLE:
lut_set_table(lut, ctrl);
break;
}
default:
return -ENOIOCTLCMD;
}
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;
if (full) {
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;
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;
}

View File

@ -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;
};

View File

@ -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) {
for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
if (pipe->inputs[i]) {
pipe->inputs[i]->pipe = NULL;
pipe->inputs[i] = 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;
if (!pipe->uds)
return;
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.
/* 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)
if (pipe->uds_input->type == VSP1_ENTITY_BRU)
alpha = 255;
if (entity->type == VSP1_ENTITY_UDS) {
struct vsp1_uds *uds = to_uds(&entity->subdev);
vsp1_uds_set_alpha(uds, dl, alpha);
break;
}
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;

View File

@ -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);

View File

@ -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

View File

@ -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,
compose = vsp1_entity_get_pad_selection(pipe->bru,
pipe->bru->config,
rpf->bru_input);
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:

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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__ */

View File

@ -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,

View File

@ -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)

View File

@ -36,6 +36,97 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf,
vsp1_dl_list_write(dl, reg + wpf->entity.index * VI6_WPF_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* 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;
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]);
/* 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, 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:

View File

@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/videodev2.h>
#include <linux/gcd.h>
#include "mt2063.h"
@ -664,27 +665,6 @@ static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
return f_Center + (bestDiff * f_Step);
}
/**
* 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);

View File

@ -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);

View File

@ -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.

View File

@ -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 */
/* 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 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name)
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 @@ static int af9035_download_firmware_old(struct dvb_usb_device *d,
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 @@ static int af9035_download_firmware_new(struct dvb_usb_device *d,
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 @@ static int af9035_read_config(struct dvb_usb_device *d)
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 @@ static int af9035_read_config(struct dvb_usb_device *d)
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 @@ static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d,
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 @@ static int af9035_init(struct dvb_usb_device *d)
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 @@ static int it930x_init(struct dvb_usb_device *d)
#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 @@ static int af9035_rc_query(struct dvb_usb_device *d)
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;
}
}

View File

@ -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];

View File

@ -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;

View File

@ -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 &&

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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,9 +1664,11 @@ 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.
*/
if (pb)
ret = call_bufop(q, verify_planes_array, *vb, pb);
if (!ret)
list_del(&(*vb)->done_entry);

View File

@ -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");

View File

@ -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,

View File

@ -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;
}
/**
* of_reserved_mem_device_init() - assign reserved memory region to given device
*
* This function assign memory region pointed by "memory-region" device tree
* property to the given device.
*/
int of_reserved_mem_device_init(struct device *dev)
{
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_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 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_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;
struct rmem_assigned_device *rd;
struct reserved_mem *rmem = NULL;
np = of_parse_phandle(dev->of_node, "memory-region", 0);
if (!np)
return;
rmem = __find_rmem(np);
of_node_put(np);
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;

View File

@ -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

View File

@ -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/

View File

@ -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.

View File

@ -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/

View File

@ -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>

View File

@ -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.

Some files were not shown because too many files have changed in this diff Show More