mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-30 19:46:42 +07:00
Merge remote-tracking branch 'media_tree/vsp1' into generic-zpos-v8
This commit is contained in:
commit
62c2cd0f49
@ -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>
|
||||
|
31
Documentation/devicetree/bindings/media/mediatek-vpu.txt
Normal file
31
Documentation/devicetree/bindings/media/mediatek-vpu.txt
Normal 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";
|
||||
};
|
32
Documentation/devicetree/bindings/media/renesas,fcp.txt
Normal file
32
Documentation/devicetree/bindings/media/renesas,fcp.txt
Normal 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>;
|
||||
};
|
@ -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
|
||||
|
||||
|
@ -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>;
|
||||
};
|
||||
|
28
MAINTAINERS
28
MAINTAINERS
@ -7357,6 +7357,16 @@ L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/potentiometer/mcp4531.c
|
||||
|
||||
MEDIA DRIVERS FOR RENESAS - FCP
|
||||
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/media/renesas,fcp.txt
|
||||
F: drivers/media/platform/rcar-fcp.c
|
||||
F: include/media/rcar-fcp.h
|
||||
|
||||
MEDIA DRIVERS FOR RENESAS - VSP1
|
||||
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -7366,8 +7376,18 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/media/renesas,vsp1.txt
|
||||
F: drivers/media/platform/vsp1/
|
||||
|
||||
MEDIA DRIVERS FOR HELENE
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
S: Supported
|
||||
F: drivers/media/dvb-frontends/helene*
|
||||
|
||||
MEDIA DRIVERS FOR ASCOT2E
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7377,6 +7397,7 @@ F: drivers/media/dvb-frontends/ascot2e*
|
||||
|
||||
MEDIA DRIVERS FOR CXD2841ER
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7386,6 +7407,7 @@ F: drivers/media/dvb-frontends/cxd2841er*
|
||||
|
||||
MEDIA DRIVERS FOR HORUS3A
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7395,6 +7417,7 @@ F: drivers/media/dvb-frontends/horus3a*
|
||||
|
||||
MEDIA DRIVERS FOR LNBH25
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7404,6 +7427,7 @@ F: drivers/media/dvb-frontends/lnbh25*
|
||||
|
||||
MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices
|
||||
M: Sergey Kozlov <serjk@netup.ru>
|
||||
M: Abylay Ospan <aospan@netup.ru>
|
||||
L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://netup.tv/
|
||||
@ -7653,10 +7677,8 @@ L: linux-media@vger.kernel.org
|
||||
W: https://linuxtv.org
|
||||
W: http://palosaari.fi/linux/
|
||||
Q: http://patchwork.linuxtv.org/project/linux-media/list/
|
||||
T: git git://linuxtv.org/anttip/media_tree.git
|
||||
S: Maintained
|
||||
F: drivers/staging/media/mn88472/
|
||||
F: drivers/media/dvb-frontends/mn88472.h
|
||||
F: drivers/media/dvb-frontends/mn88472*
|
||||
|
||||
MN88473 MEDIA DRIVER
|
||||
M: Antti Palosaari <crope@iki.fi>
|
||||
|
@ -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;
|
||||
|
@ -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 *
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
@ -26,6 +26,7 @@
|
||||
#define I2C_SLVT 1
|
||||
|
||||
#define CXD2841ER_CHIP_ID 0xa7
|
||||
#define CXD2854ER_CHIP_ID 0xc1
|
||||
|
||||
#define CXD2841ER_DVBS_POLLING_INVL 10
|
||||
|
||||
|
@ -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;
|
||||
|
1042
drivers/media/dvb-frontends/helene.c
Normal file
1042
drivers/media/dvb-frontends/helene.c
Normal file
File diff suppressed because it is too large
Load Diff
79
drivers/media/dvb-frontends/helene.h
Normal file
79
drivers/media/dvb-frontends/helene.h
Normal 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
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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], ®map_config);
|
||||
if (IS_ERR(dev->regmap[0])) {
|
||||
@ -472,15 +488,25 @@ static int mn88472_probe(struct i2c_client *client,
|
||||
goto err_kfree;
|
||||
}
|
||||
|
||||
/* check demod answers to I2C */
|
||||
ret = regmap_read(dev->regmap[0], 0x00, &utmp);
|
||||
/* Check demod answers with correct chip id */
|
||||
ret = regmap_read(dev->regmap[0], 0xff, &utmp);
|
||||
if (ret)
|
||||
goto err_regmap_0_regmap_exit;
|
||||
|
||||
dev_dbg(&client->dev, "chip id=%02x\n", utmp);
|
||||
|
||||
if (utmp != 0x02) {
|
||||
ret = -ENODEV;
|
||||
goto err_regmap_0_regmap_exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Chip has three I2C addresses for different register pages. Used
|
||||
* Chip has three I2C addresses for different register banks. Used
|
||||
* addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
|
||||
* 0x1a and 0x1c, in order to get own I2C client for each register page.
|
||||
* 0x1a and 0x1c, in order to get own I2C client for each register bank.
|
||||
*
|
||||
* Also, register bank 2 do not support sequential I/O. Only single
|
||||
* register write or read is allowed to that bank.
|
||||
*/
|
||||
dev->client[1] = i2c_new_dummy(client->adapter, 0x1a);
|
||||
if (!dev->client[1]) {
|
||||
@ -510,15 +536,25 @@ static int mn88472_probe(struct i2c_client *client,
|
||||
}
|
||||
i2c_set_clientdata(dev->client[2], dev);
|
||||
|
||||
/* create dvb_frontend */
|
||||
/* Sleep because chip is active by default */
|
||||
ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
|
||||
if (ret)
|
||||
goto err_regmap_2_regmap_exit;
|
||||
|
||||
/* Create dvb frontend */
|
||||
memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops));
|
||||
dev->fe.demodulator_priv = client;
|
||||
*config->fe = &dev->fe;
|
||||
*pdata->fe = &dev->fe;
|
||||
i2c_set_clientdata(client, dev);
|
||||
|
||||
dev_info(&client->dev, "Panasonic MN88472 successfully attached\n");
|
||||
return 0;
|
||||
/* Setup callbacks */
|
||||
pdata->get_dvb_frontend = mn88472_get_dvb_frontend;
|
||||
|
||||
dev_info(&client->dev, "Panasonic MN88472 successfully identified\n");
|
||||
|
||||
return 0;
|
||||
err_regmap_2_regmap_exit:
|
||||
regmap_exit(dev->regmap[2]);
|
||||
err_client_2_i2c_unregister_device:
|
||||
i2c_unregister_device(dev->client[2]);
|
||||
err_regmap_1_regmap_exit:
|
||||
@ -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,
|
@ -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
|
||||
|
@ -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
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
181
drivers/media/platform/rcar-fcp.c
Normal file
181
drivers/media/platform/rcar-fcp.c
Normal 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");
|
11
drivers/media/platform/rcar-vin/Kconfig
Normal file
11
drivers/media/platform/rcar-vin/Kconfig
Normal 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.
|
3
drivers/media/platform/rcar-vin/Makefile
Normal file
3
drivers/media/platform/rcar-vin/Makefile
Normal 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
|
334
drivers/media/platform/rcar-vin/rcar-core.c
Normal file
334
drivers/media/platform/rcar-vin/rcar-core.c
Normal 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");
|
1196
drivers/media/platform/rcar-vin/rcar-dma.c
Normal file
1196
drivers/media/platform/rcar-vin/rcar-dma.c
Normal file
File diff suppressed because it is too large
Load Diff
768
drivers/media/platform/rcar-vin/rcar-v4l2.c
Normal file
768
drivers/media/platform/rcar-vin/rcar-v4l2.c
Normal 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;
|
||||
}
|
163
drivers/media/platform/rcar-vin/rcar-vin.h
Normal file
163
drivers/media/platform/rcar-vin/rcar-vin.h
Normal 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
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
79
drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h
Normal file
79
drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h
Normal 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_ */
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
292
drivers/media/platform/vsp1/vsp1_clu.c
Normal file
292
drivers/media/platform/vsp1/vsp1_clu.c
Normal 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;
|
||||
}
|
48
drivers/media/platform/vsp1/vsp1_clu.h
Normal file
48
drivers/media/platform/vsp1/vsp1_clu.h
Normal 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__ */
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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__ */
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
|
@ -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 &&
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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/
|
||||
|
@ -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.
|
@ -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/
|
@ -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>
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user