[media] s5p-fimc: Add support for Exynos4x12 FIMC-LITE

This patch adds driver for FIMC-LITE camera host interface. This new IP
differs from the regular FIMC IP in that it doesn't have input DMA,
scaler and color space conversion support. So it just plain camera host
interface for MIPI-CSI2 and ITU-R interfaces. For the serial bus support
it interworks with MIPI-CSIS and the exisiting s5p-csis driver.
The FIMC-LITE and MIPI-CSIS drivers can also be reused in the Exynos5
SoC series.

Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Sylwester Nawrocki 2012-04-27 05:29:05 -03:00 committed by Mauro Carvalho Chehab
parent 5af86c2691
commit 4af813108b
7 changed files with 1766 additions and 44 deletions

View File

@ -1130,19 +1130,6 @@ config VIDEO_MX2
This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor
Interface Interface
config VIDEO_SAMSUNG_S5P_FIMC
tristate "Samsung S5P and EXYNOS4 camera interface driver (EXPERIMENTAL)"
depends on VIDEO_V4L2 && I2C && PLAT_S5P && PM_RUNTIME && \
VIDEO_V4L2_SUBDEV_API && EXPERIMENTAL
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
---help---
This is a v4l2 driver for Samsung S5P and EXYNOS4 camera
host interface and video postprocessor.
To compile this driver as a module, choose M here: the
module will be called s5p-fimc.
config VIDEO_ATMEL_ISI config VIDEO_ATMEL_ISI
tristate "ATMEL Image Sensor Interface (ISI) support" tristate "ATMEL Image Sensor Interface (ISI) support"
depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91 depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
@ -1151,16 +1138,7 @@ config VIDEO_ATMEL_ISI
This module makes the ATMEL Image Sensor Interface available This module makes the ATMEL Image Sensor Interface available
as a v4l2 device. as a v4l2 device.
config VIDEO_S5P_MIPI_CSIS source "drivers/media/video/s5p-fimc/Kconfig"
tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver"
depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P
depends on VIDEO_V4L2_SUBDEV_API && REGULATOR
---help---
This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver.
To compile this driver as a module, choose M here: the
module will be called s5p-csis.
source "drivers/media/video/s5p-tv/Kconfig" source "drivers/media/video/s5p-tv/Kconfig"
endif # V4L_PLATFORM_DRIVERS endif # V4L_PLATFORM_DRIVERS

View File

@ -0,0 +1,48 @@
config VIDEO_SAMSUNG_S5P_FIMC
bool "Samsung S5P/EXYNOS SoC camera interface driver (experimental)"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PLAT_S5P && PM_RUNTIME
depends on EXPERIMENTAL
help
Say Y here to enable camera host interface devices for
Samsung S5P and EXYNOS SoC series.
if VIDEO_SAMSUNG_S5P_FIMC
config VIDEO_S5P_FIMC
tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver"
depends on I2C
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host
interface and video postprocessor (FIMC and FIMC-LITE) devices.
To compile this driver as a module, choose M here: the
module will be called s5p-fimc.
config VIDEO_S5P_MIPI_CSIS
tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"
depends on REGULATOR
help
This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2
receiver (MIPI-CSIS) devices.
To compile this driver as a module, choose M here: the
module will be called s5p-csis.
if ARCH_EXYNOS
config VIDEO_EXYNOS_FIMC_LITE
tristate "EXYNOS FIMC-LITE camera interface driver"
depends on I2C
select VIDEOBUF2_DMA_CONTIG
help
This is a V4L2 driver for Samsung EXYNOS4/5 SoC FIMC-LITE camera
host interface.
To compile this driver as a module, choose M here: the
module will be called exynos-fimc-lite.
endif
endif # VIDEO_SAMSUNG_S5P_FIMC

View File

@ -1,5 +1,7 @@
s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o
exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o
s5p-csis-objs := mipi-csis.o s5p-csis-objs := mipi-csis.o
obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc.o obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o
obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o

View File

@ -17,6 +17,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <linux/io.h> #include <linux/io.h>
#include <asm/sizes.h>
#include <media/media-entity.h> #include <media/media-entity.h>
#include <media/videobuf2-core.h> #include <media/videobuf2-core.h>

File diff suppressed because it is too large Load Diff

View File

@ -66,6 +66,9 @@ void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me)
case CSIS_GROUP_ID: case CSIS_GROUP_ID:
p->subdevs[IDX_CSIS] = sd; p->subdevs[IDX_CSIS] = sd;
break; break;
case FLITE_GROUP_ID:
p->subdevs[IDX_FLITE] = sd;
break;
case FIMC_GROUP_ID: case FIMC_GROUP_ID:
/* No need to control FIMC subdev through subdev ops */ /* No need to control FIMC subdev through subdev ops */
break; break;
@ -336,6 +339,7 @@ static int fimc_register_callback(struct device *dev, void *p)
if (!fimc || !fimc->pdev) if (!fimc || !fimc->pdev)
return 0; return 0;
if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS) if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS)
return 0; return 0;
@ -351,6 +355,31 @@ static int fimc_register_callback(struct device *dev, void *p)
return ret; return ret;
} }
static int fimc_lite_register_callback(struct device *dev, void *p)
{
struct fimc_lite *fimc = dev_get_drvdata(dev);
struct v4l2_subdev *sd = &fimc->subdev;
struct fimc_md *fmd = p;
int ret;
if (fimc == NULL)
return 0;
if (fimc->index >= FIMC_LITE_MAX_DEVS)
return 0;
fmd->fimc_lite[fimc->index] = fimc;
sd->grp_id = FLITE_GROUP_ID;
ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
if (ret) {
v4l2_err(&fmd->v4l2_dev,
"Failed to register FIMC-LITE.%d (%d)\n",
fimc->index, ret);
}
return ret;
}
static int csis_register_callback(struct device *dev, void *p) static int csis_register_callback(struct device *dev, void *p)
{ {
struct v4l2_subdev *sd = dev_get_drvdata(dev); struct v4l2_subdev *sd = dev_get_drvdata(dev);
@ -396,6 +425,15 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd)
fimc_register_callback); fimc_register_callback);
if (ret) if (ret)
return ret; return ret;
driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type);
if (driver && try_module_get(driver->owner)) {
ret = driver_for_each_device(driver, NULL, fmd,
fimc_lite_register_callback);
if (ret)
return ret;
module_put(driver->owner);
}
/* /*
* Check if there is any sensor on the MIPI-CSI2 bus and * Check if there is any sensor on the MIPI-CSI2 bus and
* if not skip the s5p-csis module loading. * if not skip the s5p-csis module loading.
@ -433,6 +471,12 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev); v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev);
fmd->fimc[i] = NULL; fmd->fimc[i] = NULL;
} }
for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
if (fmd->fimc_lite[i] == NULL)
continue;
v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev);
fmd->fimc_lite[i] = NULL;
}
for (i = 0; i < CSIS_MAX_ENTITIES; i++) { for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
if (fmd->csis[i].sd == NULL) if (fmd->csis[i].sd == NULL)
continue; continue;
@ -456,28 +500,28 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
* @pad: the source entity pad index * @pad: the source entity pad index
* @fimc_id: index of the fimc device for which link should be enabled * @fimc_id: index of the fimc device for which link should be enabled
*/ */
static int __fimc_md_create_fimc_links(struct fimc_md *fmd, static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
struct media_entity *source, struct media_entity *source,
struct v4l2_subdev *sensor, struct v4l2_subdev *sensor,
int pad, int fimc_id) int pad, int fimc_id)
{ {
struct fimc_sensor_info *s_info; struct fimc_sensor_info *s_info;
struct media_entity *sink; struct media_entity *sink;
unsigned int flags; unsigned int flags = 0;
int ret, i; int ret, i;
for (i = 0; i < FIMC_MAX_DEVS; i++) { for (i = 0; i < FIMC_MAX_DEVS; i++) {
if (!fmd->fimc[i]) if (!fmd->fimc[i])
break; continue;
/* /*
* Some FIMC variants are not fitted with camera capture * Some FIMC variants are not fitted with camera capture
* interface. Skip creating a link from sensor for those. * interface. Skip creating a link from sensor for those.
*/ */
if (sensor->grp_id == SENSOR_GROUP_ID && if (!fmd->fimc[i]->variant->has_cam_if)
!fmd->fimc[i]->variant->has_cam_if)
continue; continue;
flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0;
sink = &fmd->fimc[i]->vid_cap.subdev.entity; sink = &fmd->fimc[i]->vid_cap.subdev.entity;
ret = media_entity_create_link(source, pad, sink, ret = media_entity_create_link(source, pad, sink,
FIMC_SD_PAD_SINK, flags); FIMC_SD_PAD_SINK, flags);
@ -493,7 +537,7 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
source->name, flags ? '=' : '-', sink->name); source->name, flags ? '=' : '-', sink->name);
if (flags == 0) if (flags == 0 || sensor == NULL)
continue; continue;
s_info = v4l2_get_subdev_hostdata(sensor); s_info = v4l2_get_subdev_hostdata(sensor);
if (!WARN_ON(s_info == NULL)) { if (!WARN_ON(s_info == NULL)) {
@ -503,9 +547,55 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
spin_unlock_irqrestore(&fmd->slock, irq_flags); spin_unlock_irqrestore(&fmd->slock, irq_flags);
} }
} }
for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
if (!fmd->fimc_lite[i])
continue;
flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0;
sink = &fmd->fimc_lite[i]->subdev.entity;
ret = media_entity_create_link(source, pad, sink,
FLITE_SD_PAD_SINK, flags);
if (ret)
return ret;
/* Notify FIMC-LITE subdev entity */
ret = media_entity_call(sink, link_setup, &sink->pads[0],
&source->pads[pad], flags);
if (ret)
break;
v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
source->name, flags ? '=' : '-', sink->name);
}
return 0; return 0;
} }
/* Create links from FIMC-LITE source pads to other entities */
static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
{
struct media_entity *source, *sink;
unsigned int flags = MEDIA_LNK_FL_ENABLED;
int i, ret;
for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
struct fimc_lite *fimc = fmd->fimc_lite[i];
if (fimc == NULL)
continue;
source = &fimc->subdev.entity;
sink = &fimc->vfd->entity;
/* FIMC-LITE's subdev and video node */
ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
sink, 0, flags);
if (ret)
break;
/* TODO: create links to other entities */
}
return ret;
}
/** /**
* fimc_md_create_links - create default links between registered entities * fimc_md_create_links - create default links between registered entities
* *
@ -562,8 +652,7 @@ static int fimc_md_create_links(struct fimc_md *fmd)
v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]", v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
sensor->entity.name, csis->entity.name); sensor->entity.name, csis->entity.name);
source = &csis->entity; source = NULL;
pad = CSIS_PAD_SOURCE;
break; break;
case FIMC_ITU_601...FIMC_ITU_656: case FIMC_ITU_601...FIMC_ITU_656:
@ -579,9 +668,21 @@ static int fimc_md_create_links(struct fimc_md *fmd)
if (source == NULL) if (source == NULL)
continue; continue;
ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad, ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
fimc_id++); pad, fimc_id++);
} }
fimc_id = 0;
for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) {
if (fmd->csis[i].sd == NULL)
continue;
source = &fmd->csis[i].sd->entity;
pad = CSIS_PAD_SOURCE;
ret = __fimc_md_create_fimc_sink_links(fmd, source, NULL,
pad, fimc_id++);
}
/* Create immutable links between each FIMC's subdev and video node */ /* Create immutable links between each FIMC's subdev and video node */
flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
for (i = 0; i < FIMC_MAX_DEVS; i++) { for (i = 0; i < FIMC_MAX_DEVS; i++) {
@ -595,7 +696,7 @@ static int fimc_md_create_links(struct fimc_md *fmd)
break; break;
} }
return ret; return __fimc_md_create_flite_source_links(fmd);
} }
/* /*
@ -703,9 +804,10 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
static int fimc_md_link_notify(struct media_pad *source, static int fimc_md_link_notify(struct media_pad *source,
struct media_pad *sink, u32 flags) struct media_pad *sink, u32 flags)
{ {
struct fimc_lite *fimc_lite = NULL;
struct fimc_dev *fimc = NULL;
struct fimc_pipeline *pipeline; struct fimc_pipeline *pipeline;
struct v4l2_subdev *sd; struct v4l2_subdev *sd;
struct fimc_dev *fimc;
int ret = 0; int ret = 0;
if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
@ -714,6 +816,10 @@ static int fimc_md_link_notify(struct media_pad *source,
sd = media_entity_to_v4l2_subdev(sink->entity); sd = media_entity_to_v4l2_subdev(sink->entity);
switch (sd->grp_id) { switch (sd->grp_id) {
case FLITE_GROUP_ID:
fimc_lite = v4l2_get_subdevdata(sd);
pipeline = &fimc_lite->pipeline;
break;
case FIMC_GROUP_ID: case FIMC_GROUP_ID:
fimc = v4l2_get_subdevdata(sd); fimc = v4l2_get_subdevdata(sd);
pipeline = &fimc->pipeline; pipeline = &fimc->pipeline;
@ -739,6 +845,7 @@ static int fimc_md_link_notify(struct media_pad *source,
* pipeline is already in use, i.e. its video node is opened. * pipeline is already in use, i.e. its video node is opened.
* Recreate the controls destroyed during the link deactivation. * Recreate the controls destroyed during the link deactivation.
*/ */
if (fimc) {
mutex_lock(&fimc->lock); mutex_lock(&fimc->lock);
if (fimc->vid_cap.refcnt > 0) { if (fimc->vid_cap.refcnt > 0) {
ret = __fimc_pipeline_initialize(pipeline, ret = __fimc_pipeline_initialize(pipeline,
@ -747,7 +854,14 @@ static int fimc_md_link_notify(struct media_pad *source,
ret = fimc_capture_ctrls_create(fimc); ret = fimc_capture_ctrls_create(fimc);
} }
mutex_unlock(&fimc->lock); mutex_unlock(&fimc->lock);
} else {
mutex_lock(&fimc_lite->lock);
if (fimc_lite->ref_count > 0) {
ret = __fimc_pipeline_initialize(pipeline,
source->entity, true);
}
mutex_unlock(&fimc_lite->lock);
}
return ret ? -EPIPE : ret; return ret ? -EPIPE : ret;
} }

View File

@ -18,13 +18,15 @@
#include <media/v4l2-subdev.h> #include <media/v4l2-subdev.h>
#include "fimc-core.h" #include "fimc-core.h"
#include "fimc-lite.h"
#include "mipi-csis.h" #include "mipi-csis.h"
/* Group IDs of sensor, MIPI CSIS and the writeback subdevs. */ /* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */
#define SENSOR_GROUP_ID (1 << 8) #define SENSOR_GROUP_ID (1 << 8)
#define CSIS_GROUP_ID (1 << 9) #define CSIS_GROUP_ID (1 << 9)
#define WRITEBACK_GROUP_ID (1 << 10) #define WRITEBACK_GROUP_ID (1 << 10)
#define FIMC_GROUP_ID (1 << 11) #define FIMC_GROUP_ID (1 << 11)
#define FLITE_GROUP_ID (1 << 12)
#define FIMC_MAX_SENSORS 8 #define FIMC_MAX_SENSORS 8
#define FIMC_MAX_CAMCLKS 2 #define FIMC_MAX_CAMCLKS 2
@ -74,6 +76,7 @@ struct fimc_md {
struct fimc_sensor_info sensor[FIMC_MAX_SENSORS]; struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
int num_sensors; int num_sensors;
struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS]; struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
struct fimc_dev *fimc[FIMC_MAX_DEVS]; struct fimc_dev *fimc[FIMC_MAX_DEVS];
struct media_device media_dev; struct media_device media_dev;
struct v4l2_device v4l2_dev; struct v4l2_device v4l2_dev;