Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull media updates from Mauro Carvalho Chehab:
 "This series contains:
   - Exynos s5p-mfc driver got support for VP8 encoder
   - Some SoC drivers gained support for asynchronous registration
     (needed for DT)
   - The RC subsystem gained support for RC activity LED;
   - New drivers added: a video decoder(adv7842), a video encoder
     (adv7511), a new GSPCA driver (stk1135) and support for Renesas
     R-Car (vsp1)
   - the first SDR kernel driver: mirics msi3101.  Due to some troubles
     with the driver, and because the API is still under discussion, it
     will be merged at staging for 3.12.  Need to rework on it
   - usual new boards additions, fixes, cleanups and driver
     improvements"

* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (242 commits)
  [media] cx88: Fix regression: CX88_AUDIO_WM8775 can't be 0
  [media] exynos4-is: Fix entity unregistration on error path
  [media] exynos-gsc: Register v4l2 device
  [media] exynos4-is: Fix fimc-lite bayer formats
  [media] em28xx: fix assignment of the eeprom data
  [media] hdpvr: fix iteration over uninitialized lists in hdpvr_probe()
  [media] usbtv: Throw corrupted frames away
  [media] usbtv: Fix deinterlacing
  [media] v4l2: added missing mutex.h include to v4l2-ctrls.h
  [media] DocBook: upgrade media_api DocBook version to 4.2
  [media] ml86v7667: fix compile warning: 'ret' set but not used
  [media] s5p-g2d: Fix registration failure
  [media] media: coda: Fix DT driver data pointer for i.MX27
  [media] s5p-mfc: Fix input/output format reporting
  [media] v4l: vsp1: Fix mutex double lock at streamon time
  [media] v4l: vsp1: Add support for RT clock
  [media] v4l: vsp1: Initialize media device bus_info field
  [media] davinci: vpif_capture: fix error return code in vpif_probe()
  [media] davinci: vpif_display: fix error return code in vpif_probe()
  [media] MAINTAINERS: add entries for adv7511 and adv7842
  ...
This commit is contained in:
Linus Torvalds 2013-09-05 11:55:59 -07:00
commit 27c053aa8d
216 changed files with 18958 additions and 2632 deletions

View file

@ -23,6 +23,8 @@ config SMS_SIANO_DEBUGFS
depends on SMS_SIANO_MDTV
depends on DEBUG_FS
depends on SMS_USB_DRV
depends on CONFIG_SMS_USB_DRV = CONFIG_SMS_SDIO_DRV
---help---
Choose Y to enable visualizing a dump of the frontend
statistics response packets via debugfs. Currently, works

View file

@ -276,7 +276,8 @@ static void smsdvb_update_per_slices(struct smsdvb_client_t *client,
/* Legacy PER/BER */
tmp = p->ets_packets * 65535;
do_div(tmp, p->ts_packets + p->ets_packets);
if (p->ts_packets + p->ets_packets)
do_div(tmp, p->ts_packets + p->ets_packets);
client->legacy_per = tmp;
}

View file

@ -369,4 +369,6 @@
#define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500
#define USB_PID_CPYTO_REDI_PC50A 0xa803
#define USB_PID_CTVDIGDUAL_V2 0xe410
#define USB_PID_PCTV_2002E 0x025c
#define USB_PID_PCTV_2002E_SE 0x025d
#endif

View file

@ -157,7 +157,6 @@ static struct regdata mb86a20s_init2[] = {
{ 0x45, 0x04 }, /* CN symbol 4 */
{ 0x48, 0x04 }, /* CN manual mode */
{ 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */
{ 0x50, 0xd6 }, { 0x51, 0x1f },
{ 0x50, 0xd2 }, { 0x51, 0x03 },
{ 0x50, 0xd7 }, { 0x51, 0xbf },
@ -1860,16 +1859,15 @@ static int mb86a20s_initfe(struct dvb_frontend *fe)
dev_dbg(&state->i2c->dev, "%s: IF=%d, IF reg=0x%06llx\n",
__func__, state->if_freq, (long long)pll);
if (!state->config->is_serial) {
if (!state->config->is_serial)
regD5 &= ~1;
rc = mb86a20s_writereg(state, 0x50, 0xd5);
if (rc < 0)
goto err;
rc = mb86a20s_writereg(state, 0x51, regD5);
if (rc < 0)
goto err;
}
rc = mb86a20s_writereg(state, 0x50, 0xd5);
if (rc < 0)
goto err;
rc = mb86a20s_writereg(state, 0x51, regD5);
if (rc < 0)
goto err;
rc = mb86a20s_writeregdata(state, mb86a20s_init2);
if (rc < 0)

View file

@ -206,6 +206,18 @@ config VIDEO_ADV7604
To compile this driver as a module, choose M here: the
module will be called adv7604.
config VIDEO_ADV7842
tristate "Analog Devices ADV7842 decoder"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
---help---
Support for the Analog Devices ADV7842 video decoder.
This is a Analog Devices Component/Graphics/SD Digitizer
with 2:1 Multiplexed HDMI Receiver.
To compile this driver as a module, choose M here: the
module will be called adv7842.
config VIDEO_BT819
tristate "BT819A VideoStream decoder"
depends on VIDEO_V4L2 && I2C
@ -417,6 +429,17 @@ config VIDEO_ADV7393
To compile this driver as a module, choose M here: the
module will be called adv7393.
config VIDEO_ADV7511
tristate "Analog Devices ADV7511 encoder"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
---help---
Support for the Analog Devices ADV7511 video encoder.
This is a Analog Devices HDMI transmitter.
To compile this driver as a module, choose M here: the
module will be called adv7511.
config VIDEO_AD9389B
tristate "Analog Devices AD9389B encoder"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API

View file

@ -26,7 +26,9 @@ obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o
obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o
obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o
obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o
obj-$(CONFIG_VIDEO_ADV7511) += adv7511.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
obj-$(CONFIG_VIDEO_BT819) += bt819.o

View file

@ -33,6 +33,7 @@
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-ctrls.h>
#include <media/ad9389b.h>
@ -442,22 +443,11 @@ static int ad9389b_log_status(struct v4l2_subdev *sd)
vic_detect, vic_sent);
}
}
if (state->dv_timings.type == V4L2_DV_BT_656_1120) {
struct v4l2_bt_timings *bt = bt = &state->dv_timings.bt;
u32 frame_width = bt->width + bt->hfrontporch +
bt->hsync + bt->hbackporch;
u32 frame_height = bt->height + bt->vfrontporch +
bt->vsync + bt->vbackporch;
u32 frame_size = frame_width * frame_height;
v4l2_info(sd, "timings: %ux%u%s%u (%ux%u). Pix freq. = %u Hz. Polarities = 0x%x\n",
bt->width, bt->height, bt->interlaced ? "i" : "p",
frame_size > 0 ? (unsigned)bt->pixelclock / frame_size : 0,
frame_width, frame_height,
(unsigned)bt->pixelclock, bt->polarities);
} else {
if (state->dv_timings.type == V4L2_DV_BT_656_1120)
v4l2_print_dv_timings(sd->name, "timings: ",
&state->dv_timings, false);
else
v4l2_info(sd, "no timings set\n");
}
return 0;
}
@ -636,95 +626,34 @@ static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
static const struct v4l2_dv_timings ad9389b_timings[] = {
V4L2_DV_BT_CEA_720X480P59_94,
V4L2_DV_BT_CEA_720X576P50,
V4L2_DV_BT_CEA_1280X720P24,
V4L2_DV_BT_CEA_1280X720P25,
V4L2_DV_BT_CEA_1280X720P30,
V4L2_DV_BT_CEA_1280X720P50,
V4L2_DV_BT_CEA_1280X720P60,
V4L2_DV_BT_CEA_1920X1080P24,
V4L2_DV_BT_CEA_1920X1080P25,
V4L2_DV_BT_CEA_1920X1080P30,
V4L2_DV_BT_CEA_1920X1080P50,
V4L2_DV_BT_CEA_1920X1080P60,
V4L2_DV_BT_DMT_640X350P85,
V4L2_DV_BT_DMT_640X400P85,
V4L2_DV_BT_DMT_720X400P85,
V4L2_DV_BT_DMT_640X480P60,
V4L2_DV_BT_DMT_640X480P72,
V4L2_DV_BT_DMT_640X480P75,
V4L2_DV_BT_DMT_640X480P85,
V4L2_DV_BT_DMT_800X600P56,
V4L2_DV_BT_DMT_800X600P60,
V4L2_DV_BT_DMT_800X600P72,
V4L2_DV_BT_DMT_800X600P75,
V4L2_DV_BT_DMT_800X600P85,
V4L2_DV_BT_DMT_848X480P60,
V4L2_DV_BT_DMT_1024X768P60,
V4L2_DV_BT_DMT_1024X768P70,
V4L2_DV_BT_DMT_1024X768P75,
V4L2_DV_BT_DMT_1024X768P85,
V4L2_DV_BT_DMT_1152X864P75,
V4L2_DV_BT_DMT_1280X768P60_RB,
V4L2_DV_BT_DMT_1280X768P60,
V4L2_DV_BT_DMT_1280X768P75,
V4L2_DV_BT_DMT_1280X768P85,
V4L2_DV_BT_DMT_1280X800P60_RB,
V4L2_DV_BT_DMT_1280X800P60,
V4L2_DV_BT_DMT_1280X800P75,
V4L2_DV_BT_DMT_1280X800P85,
V4L2_DV_BT_DMT_1280X960P60,
V4L2_DV_BT_DMT_1280X960P85,
V4L2_DV_BT_DMT_1280X1024P60,
V4L2_DV_BT_DMT_1280X1024P75,
V4L2_DV_BT_DMT_1280X1024P85,
V4L2_DV_BT_DMT_1360X768P60,
V4L2_DV_BT_DMT_1400X1050P60_RB,
V4L2_DV_BT_DMT_1400X1050P60,
V4L2_DV_BT_DMT_1400X1050P75,
V4L2_DV_BT_DMT_1400X1050P85,
V4L2_DV_BT_DMT_1440X900P60_RB,
V4L2_DV_BT_DMT_1440X900P60,
V4L2_DV_BT_DMT_1600X1200P60,
V4L2_DV_BT_DMT_1680X1050P60_RB,
V4L2_DV_BT_DMT_1680X1050P60,
V4L2_DV_BT_DMT_1792X1344P60,
V4L2_DV_BT_DMT_1856X1392P60,
V4L2_DV_BT_DMT_1920X1200P60_RB,
V4L2_DV_BT_DMT_1366X768P60,
V4L2_DV_BT_DMT_1920X1080P60,
{},
static const struct v4l2_dv_timings_cap ad9389b_timings_cap = {
.type = V4L2_DV_BT_656_1120,
.bt = {
.max_width = 1920,
.max_height = 1200,
.min_pixelclock = 25000000,
.max_pixelclock = 170000000,
.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM,
},
};
static int ad9389b_s_dv_timings(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings)
{
struct ad9389b_state *state = get_ad9389b_state(sd);
int i;
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
/* quick sanity check */
if (timings->type != V4L2_DV_BT_656_1120)
return -EINVAL;
if (timings->bt.interlaced)
return -EINVAL;
if (timings->bt.pixelclock < 27000000 ||
timings->bt.pixelclock > 170000000)
if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap, NULL, NULL))
return -EINVAL;
/* Fill the optional fields .standards and .flags in struct v4l2_dv_timings
if the format is listed in ad9389b_timings[] */
for (i = 0; ad9389b_timings[i].bt.width; i++) {
if (v4l_match_dv_timings(timings, &ad9389b_timings[i], 0)) {
*timings = ad9389b_timings[i];
break;
}
}
if the format is one of the CEA or DMT timings. */
v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0, NULL, NULL);
timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS;
@ -762,26 +691,14 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd,
static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd,
struct v4l2_enum_dv_timings *timings)
{
if (timings->index >= ARRAY_SIZE(ad9389b_timings))
return -EINVAL;
memset(timings->reserved, 0, sizeof(timings->reserved));
timings->timings = ad9389b_timings[timings->index];
return 0;
return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap,
NULL, NULL);
}
static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
cap->type = V4L2_DV_BT_656_1120;
cap->bt.max_width = 1920;
cap->bt.max_height = 1200;
cap->bt.min_pixelclock = 27000000;
cap->bt.max_pixelclock = 170000000;
cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT;
cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM;
*cap = ad9389b_timings_cap;
return 0;
}
@ -930,8 +847,10 @@ static void ad9389b_edid_handler(struct work_struct *work)
* (DVI connectors are particularly prone to this problem). */
if (state->edid.read_retries) {
state->edid.read_retries--;
/* EDID read failed, trigger a retry */
ad9389b_wr(sd, 0xc9, 0xf);
v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__);
state->have_monitor = false;
ad9389b_s_power(sd, false);
ad9389b_s_power(sd, true);
queue_delayed_work(state->work_queue,
&state->edid_handler, EDID_DELAY);
return;
@ -967,11 +886,9 @@ static void ad9389b_setup(struct v4l2_subdev *sd)
ad9389b_wr_and_or(sd, 0x15, 0xf1, 0x0);
/* Output format: RGB 4:4:4 */
ad9389b_wr_and_or(sd, 0x16, 0x3f, 0x0);
/* CSC fixed point: +/-2, 1st order interpolation 4:2:2 -> 4:4:4 up
conversion, Aspect ratio: 16:9 */
ad9389b_wr_and_or(sd, 0x17, 0xe1, 0x0e);
/* Disable pixel repetition and CSC */
ad9389b_wr_and_or(sd, 0x3b, 0x9e, 0x0);
/* 1st order interpolation 4:2:2 -> 4:4:4 up conversion,
Aspect ratio: 16:9 */
ad9389b_wr_and_or(sd, 0x17, 0xf9, 0x06);
/* Output format: RGB 4:4:4, Active Format Information is valid. */
ad9389b_wr_and_or(sd, 0x45, 0xc7, 0x08);
/* Underscanned */
@ -1056,12 +973,12 @@ static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd)
static bool edid_block_verify_crc(u8 *edid_block)
{
int i;
u8 sum = 0;
int i;
for (i = 0; i < 127; i++)
sum += *(edid_block + i);
return ((255 - sum + 1) == edid_block[127]);
for (i = 0; i < 128; i++)
sum += edid_block[i];
return sum == 0;
}
static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment)
@ -1107,6 +1024,8 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)
}
if (!edid_segment_verify_crc(sd, segment)) {
/* edid crc error, force reread of edid segment */
v4l2_err(sd, "%s: edid crc error\n", __func__);
state->have_monitor = false;
ad9389b_s_power(sd, false);
ad9389b_s_power(sd, true);
return false;
@ -1190,27 +1109,27 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *
state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops,
V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI,
0, V4L2_DV_TX_MODE_DVI_D);
state->hdmi_mode_ctrl->is_private = true;
state->hotplug_ctrl = v4l2_ctrl_new_std(hdl, NULL,
V4L2_CID_DV_TX_HOTPLUG, 0, 1, 0, 0);
state->hotplug_ctrl->is_private = true;
state->rx_sense_ctrl = v4l2_ctrl_new_std(hdl, NULL,
V4L2_CID_DV_TX_RXSENSE, 0, 1, 0, 0);
state->rx_sense_ctrl->is_private = true;
state->have_edid0_ctrl = v4l2_ctrl_new_std(hdl, NULL,
V4L2_CID_DV_TX_EDID_PRESENT, 0, 1, 0, 0);
state->have_edid0_ctrl->is_private = true;
state->rgb_quantization_range_ctrl =
v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops,
V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
0, V4L2_DV_RGB_RANGE_AUTO);
state->rgb_quantization_range_ctrl->is_private = true;
sd->ctrl_handler = hdl;
if (hdl->error) {
err = hdl->error;
goto err_hdl;
}
state->hdmi_mode_ctrl->is_private = true;
state->hotplug_ctrl->is_private = true;
state->rx_sense_ctrl->is_private = true;
state->have_edid0_ctrl->is_private = true;
state->rgb_quantization_range_ctrl->is_private = true;
state->pad.flags = MEDIA_PAD_FL_SINK;
err = media_entity_init(&sd->entity, 1, &state->pad, 0);

View file

@ -27,8 +27,10 @@
#include <linux/uaccess.h>
#include <media/adv7343.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-of.h>
#include "adv7343_regs.h"
@ -226,12 +228,12 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type)
else
val = state->pdata->mode_config.sleep_mode << 0 |
state->pdata->mode_config.pll_control << 1 |
state->pdata->mode_config.dac_3 << 2 |
state->pdata->mode_config.dac_2 << 3 |
state->pdata->mode_config.dac_1 << 4 |
state->pdata->mode_config.dac_6 << 5 |
state->pdata->mode_config.dac_5 << 6 |
state->pdata->mode_config.dac_4 << 7;
state->pdata->mode_config.dac[2] << 2 |
state->pdata->mode_config.dac[1] << 3 |
state->pdata->mode_config.dac[0] << 4 |
state->pdata->mode_config.dac[5] << 5 |
state->pdata->mode_config.dac[4] << 6 |
state->pdata->mode_config.dac[3] << 7;
err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val);
if (err < 0)
@ -250,15 +252,15 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type)
/* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */
val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI);
if (state->pdata && state->pdata->sd_config.sd_dac_out1)
val = val | (state->pdata->sd_config.sd_dac_out1 << 1);
else if (state->pdata && !state->pdata->sd_config.sd_dac_out1)
val = val & ~(state->pdata->sd_config.sd_dac_out1 << 1);
if (state->pdata && state->pdata->sd_config.sd_dac_out[0])
val = val | (state->pdata->sd_config.sd_dac_out[0] << 1);
else if (state->pdata && !state->pdata->sd_config.sd_dac_out[0])
val = val & ~(state->pdata->sd_config.sd_dac_out[0] << 1);
if (state->pdata && state->pdata->sd_config.sd_dac_out2)
val = val | (state->pdata->sd_config.sd_dac_out2 << 2);
else if (state->pdata && !state->pdata->sd_config.sd_dac_out2)
val = val & ~(state->pdata->sd_config.sd_dac_out2 << 2);
if (state->pdata && state->pdata->sd_config.sd_dac_out[1])
val = val | (state->pdata->sd_config.sd_dac_out[1] << 2);
else if (state->pdata && !state->pdata->sd_config.sd_dac_out[1])
val = val & ~(state->pdata->sd_config.sd_dac_out[1] << 2);
err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val);
if (err < 0)
@ -398,6 +400,40 @@ static int adv7343_initialize(struct v4l2_subdev *sd)
return err;
}
static struct adv7343_platform_data *
adv7343_get_pdata(struct i2c_client *client)
{
struct adv7343_platform_data *pdata;
struct device_node *np;
if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
return client->dev.platform_data;
np = v4l2_of_get_next_endpoint(client->dev.of_node, NULL);
if (!np)
return NULL;
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
goto done;
pdata->mode_config.sleep_mode =
of_property_read_bool(np, "adi,power-mode-sleep-mode");
pdata->mode_config.pll_control =
of_property_read_bool(np, "adi,power-mode-pll-ctrl");
of_property_read_u32_array(np, "adi,dac-enable",
pdata->mode_config.dac, 6);
of_property_read_u32_array(np, "adi,sd-dac-enable",
pdata->sd_config.sd_dac_out, 2);
done:
of_node_put(np);
return pdata;
}
static int adv7343_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@ -416,7 +452,7 @@ static int adv7343_probe(struct i2c_client *client,
return -ENOMEM;
/* Copy board specific information here */
state->pdata = client->dev.platform_data;
state->pdata = adv7343_get_pdata(client);
state->reg00 = 0x80;
state->reg01 = 0x00;
@ -445,16 +481,21 @@ static int adv7343_probe(struct i2c_client *client,
ADV7343_GAIN_DEF);
state->sd.ctrl_handler = &state->hdl;
if (state->hdl.error) {
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
return err;
err = state->hdl.error;
goto done;
}
v4l2_ctrl_handler_setup(&state->hdl);
err = adv7343_initialize(&state->sd);
if (err)
goto done;
err = v4l2_async_register_subdev(&state->sd);
done:
if (err < 0)
v4l2_ctrl_handler_free(&state->hdl);
return err;
}
@ -463,6 +504,7 @@ static int adv7343_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7343_state *state = to_state(sd);
v4l2_async_unregister_subdev(&state->sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
@ -476,8 +518,17 @@ static const struct i2c_device_id adv7343_id[] = {
MODULE_DEVICE_TABLE(i2c, adv7343_id);
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id adv7343_of_match[] = {
{.compatible = "adi,adv7343", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, adv7343_of_match);
#endif
static struct i2c_driver adv7343_driver = {
.driver = {
.of_match_table = of_match_ptr(adv7343_of_match),
.owner = THIS_MODULE,
.name = "adv7343",
},

1198
drivers/media/i2c/adv7511.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -38,6 +38,7 @@
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-dv-timings.h>
#include <media/adv7604.h>
static int debug;
@ -76,6 +77,7 @@ struct adv7604_state {
struct delayed_work delayed_work_enable_hotplug;
bool connector_hdmi;
bool restart_stdi_once;
u32 prev_input_status;
/* i2c clients */
struct i2c_client *i2c_avlink;
@ -260,22 +262,22 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
static inline unsigned hblanking(const struct v4l2_bt_timings *t)
{
return t->hfrontporch + t->hsync + t->hbackporch;
return V4L2_DV_BT_BLANKING_WIDTH(t);
}
static inline unsigned htotal(const struct v4l2_bt_timings *t)
{
return t->width + t->hfrontporch + t->hsync + t->hbackporch;
return V4L2_DV_BT_FRAME_WIDTH(t);
}
static inline unsigned vblanking(const struct v4l2_bt_timings *t)
{
return t->vfrontporch + t->vsync + t->vbackporch;
return V4L2_DV_BT_BLANKING_HEIGHT(t);
}
static inline unsigned vtotal(const struct v4l2_bt_timings *t)
{
return t->height + t->vfrontporch + t->vsync + t->vbackporch;
return V4L2_DV_BT_FRAME_HEIGHT(t);
}
/* ----------------------------------------------------------------------- */
@ -761,7 +763,7 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
int i;
for (i = 0; predef_vid_timings[i].timings.bt.width; i++) {
if (!v4l_match_dv_timings(timings, &predef_vid_timings[i].timings,
if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings,
DIGITAL_INPUT ? 250000 : 1000000))
continue;
io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */
@ -990,6 +992,11 @@ static inline bool no_lock_tmds(struct v4l2_subdev *sd)
return (io_read(sd, 0x6a) & 0xe0) != 0xe0;
}
static inline bool is_hdmi(struct v4l2_subdev *sd)
{
return hdmi_read(sd, 0x05) & 0x80;
}
static inline bool no_lock_sspd(struct v4l2_subdev *sd)
{
/* TODO channel 2 */
@ -1044,38 +1051,6 @@ static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status)
/* ----------------------------------------------------------------------- */
static void adv7604_print_timings(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings, const char *txt, bool detailed)
{
struct v4l2_bt_timings *bt = &timings->bt;
u32 htot, vtot;
if (timings->type != V4L2_DV_BT_656_1120)
return;
htot = htotal(bt);
vtot = vtotal(bt);
v4l2_info(sd, "%s %dx%d%s%d (%dx%d)",
txt, bt->width, bt->height, bt->interlaced ? "i" : "p",
(htot * vtot) > 0 ? ((u32)bt->pixelclock /
(htot * vtot)) : 0,
htot, vtot);
if (detailed) {
v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n",
bt->hfrontporch,
(bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-",
bt->hsync, bt->hbackporch);
v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n",
bt->vfrontporch,
(bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-",
bt->vsync, bt->vbackporch);
v4l2_info(sd, " pixelclock: %lld, flags: 0x%x, standards: 0x%x\n",
bt->pixelclock, bt->flags, bt->standards);
}
}
struct stdi_readback {
u16 bl, lcf, lcvs;
u8 hs_pol, vs_pol;
@ -1187,7 +1162,7 @@ static int adv7604_dv_timings_cap(struct v4l2_subdev *sd,
cap->type = V4L2_DV_BT_656_1120;
cap->bt.max_width = 1920;
cap->bt.max_height = 1200;
cap->bt.min_pixelclock = 27000000;
cap->bt.min_pixelclock = 25000000;
if (DIGITAL_INPUT)
cap->bt.max_pixelclock = 225000000;
else
@ -1208,7 +1183,7 @@ static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
int i;
for (i = 0; adv7604_timings[i].bt.width; i++) {
if (v4l_match_dv_timings(timings, &adv7604_timings[i],
if (v4l2_match_dv_timings(timings, &adv7604_timings[i],
DIGITAL_INPUT ? 250000 : 1000000)) {
*timings = adv7604_timings[i];
break;
@ -1242,12 +1217,21 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
if (DIGITAL_INPUT) {
uint32_t freq;
timings->type = V4L2_DV_BT_656_1120;
bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08);
bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a);
bt->pixelclock = (hdmi_read(sd, 0x06) * 1000000) +
freq = (hdmi_read(sd, 0x06) * 1000000) +
((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000;
if (is_hdmi(sd)) {
/* adjust for deep color mode */
unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8;
freq = freq * 8 / bits_per_channel;
}
bt->pixelclock = freq;
bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 +
hdmi_read(sd, 0x21);
bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 +
@ -1329,8 +1313,8 @@ found:
}
if (debug > 1)
adv7604_print_timings(sd, timings,
"adv7604_query_dv_timings:", true);
v4l2_print_dv_timings(sd->name, "adv7604_query_dv_timings: ",
timings, true);
return 0;
}
@ -1372,8 +1356,8 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd,
if (debug > 1)
adv7604_print_timings(sd, timings,
"adv7604_s_dv_timings:", true);
v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ",
timings, true);
return 0;
}
@ -1534,6 +1518,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
{
struct adv7604_state *state = to_state(sd);
u8 fmt_change, fmt_change_digital, tx_5v;
u32 input_status;
/* format change */
fmt_change = io_read(sd, 0x43) & 0x98;
@ -1544,9 +1529,18 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
io_write(sd, 0x6c, fmt_change_digital);
if (fmt_change || fmt_change_digital) {
v4l2_dbg(1, debug, sd,
"%s: ADV7604_FMT_CHANGE, fmt_change = 0x%x, fmt_change_digital = 0x%x\n",
"%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n",
__func__, fmt_change, fmt_change_digital);
v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL);
adv7604_g_input_status(sd, &input_status);
if (input_status != state->prev_input_status) {
v4l2_dbg(1, debug, sd,
"%s: input_status = 0x%x, prev_input_status = 0x%x\n",
__func__, input_status, state->prev_input_status);
state->prev_input_status = input_status;
v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL);
}
if (handled)
*handled = true;
}
@ -1625,7 +1619,7 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)
u8 avi_len;
u8 avi_ver;
if (!(hdmi_read(sd, 0x05) & 0x80)) {
if (!is_hdmi(sd)) {
v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n");
return;
}
@ -1686,6 +1680,12 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
"RGB limited range (16-235)",
"RGB full range (0-255)",
};
char *deep_color_mode_txt[4] = {
"8-bits per channel",
"10-bits per channel",
"12-bits per channel",
"16-bits per channel (not supported)"
};
v4l2_info(sd, "-----Chip status-----\n");
v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on");
@ -1723,8 +1723,13 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
if (adv7604_query_dv_timings(sd, &timings))
v4l2_info(sd, "No video detected\n");
else
adv7604_print_timings(sd, &timings, "Detected format:", true);
adv7604_print_timings(sd, &state->timings, "Configured format:", true);
v4l2_print_dv_timings(sd->name, "Detected format: ",
&timings, true);
v4l2_print_dv_timings(sd->name, "Configured format: ",
&state->timings, true);
if (no_signal(sd))
return 0;
v4l2_info(sd, "-----Color space-----\n");
v4l2_info(sd, "RGB quantization range ctrl: %s\n",
@ -1735,15 +1740,40 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
(reg_io_0x02 & 0x02) ? "RGB" : "YCbCr",
(reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)",
((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ?
"enabled" : "disabled");
"enabled" : "disabled");
v4l2_info(sd, "Color space conversion: %s\n",
csc_coeff_sel_rb[cp_read(sd, 0xfc) >> 4]);
/* Digital video */
if (DIGITAL_INPUT) {
v4l2_info(sd, "-----HDMI status-----\n");
v4l2_info(sd, "HDCP encrypted content: %s\n",
hdmi_read(sd, 0x05) & 0x40 ? "true" : "false");
if (!DIGITAL_INPUT)
return 0;
v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D");
v4l2_info(sd, "HDCP encrypted content: %s\n", (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false");
v4l2_info(sd, "HDCP keys read: %s%s\n",
(hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no",
(hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : "");
if (!is_hdmi(sd)) {
bool audio_pll_locked = hdmi_read(sd, 0x04) & 0x01;
bool audio_sample_packet_detect = hdmi_read(sd, 0x18) & 0x01;
bool audio_mute = io_read(sd, 0x65) & 0x40;
v4l2_info(sd, "Audio: pll %s, samples %s, %s\n",
audio_pll_locked ? "locked" : "not locked",
audio_sample_packet_detect ? "detected" : "not detected",
audio_mute ? "muted" : "enabled");
if (audio_pll_locked && audio_sample_packet_detect) {
v4l2_info(sd, "Audio format: %s\n",
(hdmi_read(sd, 0x07) & 0x20) ? "multi-channel" : "stereo");
}
v4l2_info(sd, "Audio CTS: %u\n", (hdmi_read(sd, 0x5b) << 12) +
(hdmi_read(sd, 0x5c) << 8) +
(hdmi_read(sd, 0x5d) & 0xf0));
v4l2_info(sd, "Audio N: %u\n", ((hdmi_read(sd, 0x5d) & 0x0f) << 16) +
(hdmi_read(sd, 0x5e) << 8) +
hdmi_read(sd, 0x5f));
v4l2_info(sd, "AV Mute: %s\n", (hdmi_read(sd, 0x04) & 0x40) ? "on" : "off");
v4l2_info(sd, "Deep color mode: %s\n", deep_color_mode_txt[(hdmi_read(sd, 0x0b) & 0x60) >> 5]);
print_avi_infoframe(sd);
}
@ -1952,6 +1982,10 @@ static int adv7604_probe(struct i2c_client *client,
return -ENOMEM;
}
/* initialize variables */
state->restart_stdi_once = true;
state->prev_input_status = ~0;
/* platform data */
if (!pdata) {
v4l_err(client, "No platform data!\n");
@ -1987,29 +2021,30 @@ static int adv7604_probe(struct i2c_client *client,
/* private controls */
state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0);
state->detect_tx_5v_ctrl->is_private = true;
state->rgb_quantization_range_ctrl =
v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops,
V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
0, V4L2_DV_RGB_RANGE_AUTO);
state->rgb_quantization_range_ctrl->is_private = true;
/* custom controls */
state->analog_sampling_phase_ctrl =
v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL);
state->analog_sampling_phase_ctrl->is_private = true;
state->free_run_color_manual_ctrl =
v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL);
state->free_run_color_manual_ctrl->is_private = true;
state->free_run_color_ctrl =
v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color, NULL);
state->free_run_color_ctrl->is_private = true;
sd->ctrl_handler = hdl;
if (hdl->error) {
err = hdl->error;
goto err_hdl;
}
state->detect_tx_5v_ctrl->is_private = true;
state->rgb_quantization_range_ctrl->is_private = true;
state->analog_sampling_phase_ctrl->is_private = true;
state->free_run_color_manual_ctrl->is_private = true;
state->free_run_color_ctrl->is_private = true;
if (adv7604_s_detect_tx_5v_ctrl(sd)) {
err = -ENODEV;
goto err_hdl;
@ -2035,7 +2070,6 @@ static int adv7604_probe(struct i2c_client *client,
v4l2_err(sd, "failed to create all i2c clients\n");
goto err_i2c;
}
state->restart_stdi_once = true;
/* work queues */
state->work_queues = create_singlethread_workqueue(client->name);

2946
drivers/media/i2c/adv7842.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -209,7 +209,8 @@ static int ml86v7667_mbus_fmt(struct v4l2_subdev *sd,
fmt->code = V4L2_MBUS_FMT_YUYV8_2X8;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
fmt->field = V4L2_FIELD_INTERLACED;
/* The top field is always transferred first by the chip */
fmt->field = V4L2_FIELD_INTERLACED_TB;
fmt->width = 720;
fmt->height = priv->std & V4L2_STD_525_60 ? 480 : 576;

View file

@ -12,6 +12,7 @@
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/log2.h>
@ -135,6 +136,8 @@ struct mt9v032 {
struct mutex power_lock;
int power_count;
struct clk *clk;
struct mt9v032_platform_data *pdata;
u32 sysclk;
@ -219,10 +222,9 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032)
struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
int ret;
if (mt9v032->pdata->set_clock) {
mt9v032->pdata->set_clock(&mt9v032->subdev, mt9v032->sysclk);
udelay(1);
}
clk_set_rate(mt9v032->clk, mt9v032->sysclk);
clk_prepare_enable(mt9v032->clk);
udelay(1);
/* Reset the chip and stop data read out */
ret = mt9v032_write(client, MT9V032_RESET, 1);
@ -238,8 +240,7 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032)
static void mt9v032_power_off(struct mt9v032 *mt9v032)
{
if (mt9v032->pdata->set_clock)
mt9v032->pdata->set_clock(&mt9v032->subdev, 0);
clk_disable_unprepare(mt9v032->clk);
}
static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on)
@ -748,6 +749,10 @@ static int mt9v032_probe(struct i2c_client *client,
if (!mt9v032)
return -ENOMEM;
mt9v032->clk = devm_clk_get(&client->dev, NULL);
if (IS_ERR(mt9v032->clk))
return PTR_ERR(mt9v032->clk);
mutex_init(&mt9v032->power_lock);
mt9v032->pdata = pdata;

View file

@ -1083,7 +1083,7 @@ static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd,
{
int i = ARRAY_SIZE(ov965x_formats);
if (fse->index > ARRAY_SIZE(ov965x_framesizes))
if (fse->index >= ARRAY_SIZE(ov965x_framesizes))
return -EINVAL;
while (--i)

View file

@ -1111,6 +1111,11 @@ static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd,
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
mf = v4l2_subdev_get_try_format(fh, fmt->pad);
*mf = fmt->format;
if (fmt->pad == OIF_ISP_PAD) {
mf = v4l2_subdev_get_try_format(fh, OIF_SOURCE_PAD);
mf->width = fmt->format.width;
mf->height = fmt->format.height;
}
} else {
switch (fmt->pad) {
case OIF_ISP_PAD:

View file

@ -1003,7 +1003,7 @@ static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd,
const struct s5k6aa_interval *fi;
int ret = 0;
if (fie->index > ARRAY_SIZE(s5k6aa_intervals))
if (fie->index >= ARRAY_SIZE(s5k6aa_intervals))
return -EINVAL;
v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN,

View file

@ -225,19 +225,27 @@ static const unsigned char saa7111_init[] = {
0x00, 0x00
};
/* SAA7113/GM7113C init codes
* It's important that R_14... R_17 == 0x00
* for the gm7113c chip to deliver stable video
/*
* This table has one illegal value, and some values that are not
* correct according to the datasheet initialization table.
*
* If you need a table with legal/default values tell the driver in
* i2c_board_info.platform_data, and you will get the gm7113c_init
* table instead.
*/
/* SAA7113 Init codes */
static const unsigned char saa7113_init[] = {
R_01_INC_DELAY, 0x08,
R_02_INPUT_CNTL_1, 0xc2,
R_03_INPUT_CNTL_2, 0x30,
R_04_INPUT_CNTL_3, 0x00,
R_05_INPUT_CNTL_4, 0x00,
R_06_H_SYNC_START, 0x89,
R_06_H_SYNC_START, 0x89, /* Illegal value -119,
* min. value = -108 (0x94) */
R_07_H_SYNC_STOP, 0x0d,
R_08_SYNC_CNTL, 0x88,
R_08_SYNC_CNTL, 0x88, /* Not datasheet default.
* HTC = VTR mode, should be 0x98 */
R_09_LUMA_CNTL, 0x01,
R_0A_LUMA_BRIGHT_CNTL, 0x80,
R_0B_LUMA_CONTRAST_CNTL, 0x47,
@ -245,9 +253,45 @@ static const unsigned char saa7113_init[] = {
R_0D_CHROMA_HUE_CNTL, 0x00,
R_0E_CHROMA_CNTL_1, 0x01,
R_0F_CHROMA_GAIN_CNTL, 0x2a,
R_10_CHROMA_CNTL_2, 0x08,
R_10_CHROMA_CNTL_2, 0x08, /* Not datsheet default.
* VRLN enabled, should be 0x00 */
R_11_MODE_DELAY_CNTL, 0x0c,
R_12_RT_SIGNAL_CNTL, 0x07,
R_12_RT_SIGNAL_CNTL, 0x07, /* Not datasheet default,
* should be 0x01 */
R_13_RT_X_PORT_OUT_CNTL, 0x00,
R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
R_15_VGATE_START_FID_CHG, 0x00,
R_16_VGATE_STOP, 0x00,
R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
0x00, 0x00
};
/*
* GM7113C is a clone of the SAA7113 chip
* This init table is copied out of the saa7113 datasheet.
* In R_08 we enable "Automatic Field Detection" [AUFD],
* this is disabled when saa711x_set_v4lstd is called.
*/
static const unsigned char gm7113c_init[] = {
R_01_INC_DELAY, 0x08,
R_02_INPUT_CNTL_1, 0xc0,
R_03_INPUT_CNTL_2, 0x33,
R_04_INPUT_CNTL_3, 0x00,
R_05_INPUT_CNTL_4, 0x00,
R_06_H_SYNC_START, 0xe9,
R_07_H_SYNC_STOP, 0x0d,
R_08_SYNC_CNTL, 0x98,
R_09_LUMA_CNTL, 0x01,
R_0A_LUMA_BRIGHT_CNTL, 0x80,
R_0B_LUMA_CONTRAST_CNTL, 0x47,
R_0C_CHROMA_SAT_CNTL, 0x40,
R_0D_CHROMA_HUE_CNTL, 0x00,
R_0E_CHROMA_CNTL_1, 0x01,
R_0F_CHROMA_GAIN_CNTL, 0x2a,
R_10_CHROMA_CNTL_2, 0x00,
R_11_MODE_DELAY_CNTL, 0x0c,
R_12_RT_SIGNAL_CNTL, 0x01,
R_13_RT_X_PORT_OUT_CNTL, 0x00,
R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
R_15_VGATE_START_FID_CHG, 0x00,
@ -462,24 +506,6 @@ static const unsigned char saa7115_cfg_50hz_video[] = {
/* ============== SAA7715 VIDEO templates (end) ======= */
/* ============== GM7113C VIDEO templates ============= */
static const unsigned char gm7113c_cfg_60hz_video[] = {
R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */
R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */
0x00, 0x00
};
static const unsigned char gm7113c_cfg_50hz_video[] = {
R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */
R_0E_CHROMA_CNTL_1, 0x07,
0x00, 0x00
};
/* ============== GM7113C VIDEO templates (end) ======= */
static const unsigned char saa7115_cfg_vbi_on[] = {
R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
@ -964,17 +990,24 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
// This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
if (std & V4L2_STD_525_60) {
v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
if (state->ident == GM7113C)
saa711x_writeregs(sd, gm7113c_cfg_60hz_video);
else
if (state->ident == GM7113C) {
u8 reg = saa711x_read(sd, R_08_SYNC_CNTL);
reg &= ~(SAA7113_R_08_FSEL | SAA7113_R_08_AUFD);
reg |= SAA7113_R_08_FSEL;
saa711x_write(sd, R_08_SYNC_CNTL, reg);
} else {
saa711x_writeregs(sd, saa7115_cfg_60hz_video);
}
saa711x_set_size(sd, 720, 480);
} else {
v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
if (state->ident == GM7113C)
saa711x_writeregs(sd, gm7113c_cfg_50hz_video);
else
if (state->ident == GM7113C) {
u8 reg = saa711x_read(sd, R_08_SYNC_CNTL);
reg &= ~(SAA7113_R_08_FSEL | SAA7113_R_08_AUFD);
saa711x_write(sd, R_08_SYNC_CNTL, reg);
} else {
saa711x_writeregs(sd, saa7115_cfg_50hz_video);
}
saa711x_set_size(sd, 720, 576);
}
@ -1596,6 +1629,65 @@ static const struct v4l2_subdev_ops saa711x_ops = {
/* ----------------------------------------------------------------------- */
static void saa711x_write_platform_data(struct saa711x_state *state,
struct saa7115_platform_data *data)
{
struct v4l2_subdev *sd = &state->sd;
u8 work;
if (state->ident != GM7113C &&
state->ident != SAA7113)
return;
if (data->saa7113_r08_htc) {
work = saa711x_read(sd, R_08_SYNC_CNTL);
work &= ~SAA7113_R_08_HTC_MASK;
work |= ((*data->saa7113_r08_htc) << SAA7113_R_08_HTC_OFFSET);
saa711x_write(sd, R_08_SYNC_CNTL, work);
}
if (data->saa7113_r10_vrln) {
work = saa711x_read(sd, R_10_CHROMA_CNTL_2);
work &= ~SAA7113_R_10_VRLN_MASK;
if (*data->saa7113_r10_vrln)
work |= (1 << SAA7113_R_10_VRLN_OFFSET);
saa711x_write(sd, R_10_CHROMA_CNTL_2, work);
}
if (data->saa7113_r10_ofts) {
work = saa711x_read(sd, R_10_CHROMA_CNTL_2);
work &= ~SAA7113_R_10_OFTS_MASK;
work |= (*data->saa7113_r10_ofts << SAA7113_R_10_OFTS_OFFSET);
saa711x_write(sd, R_10_CHROMA_CNTL_2, work);
}
if (data->saa7113_r12_rts0) {
work = saa711x_read(sd, R_12_RT_SIGNAL_CNTL);
work &= ~SAA7113_R_12_RTS0_MASK;
work |= (*data->saa7113_r12_rts0 << SAA7113_R_12_RTS0_OFFSET);
/* According to the datasheet,
* SAA7113_RTS_DOT_IN should only be used on RTS1 */
WARN_ON(*data->saa7113_r12_rts0 == SAA7113_RTS_DOT_IN);
saa711x_write(sd, R_12_RT_SIGNAL_CNTL, work);
}
if (data->saa7113_r12_rts1) {
work = saa711x_read(sd, R_12_RT_SIGNAL_CNTL);
work &= ~SAA7113_R_12_RTS1_MASK;
work |= (*data->saa7113_r12_rts1 << SAA7113_R_12_RTS1_OFFSET);
saa711x_write(sd, R_12_RT_SIGNAL_CNTL, work);
}
if (data->saa7113_r13_adlsb) {
work = saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL);
work &= ~SAA7113_R_13_ADLSB_MASK;
if (*data->saa7113_r13_adlsb)
work |= (1 << SAA7113_R_13_ADLSB_OFFSET);
saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, work);
}
}
/**
* saa711x_detect_chip - Detects the saa711x (or clone) variant
* @client: I2C client structure.
@ -1704,6 +1796,7 @@ static int saa711x_probe(struct i2c_client *client,
struct saa711x_state *state;
struct v4l2_subdev *sd;
struct v4l2_ctrl_handler *hdl;
struct saa7115_platform_data *pdata;
int ident;
char name[CHIP_VER_SIZE + 1];
@ -1767,21 +1860,31 @@ static int saa711x_probe(struct i2c_client *client,
/* init to 60hz/48khz */
state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
pdata = client->dev.platform_data;
switch (state->ident) {
case SAA7111:
case SAA7111A:
saa711x_writeregs(sd, saa7111_init);
break;
case GM7113C:
saa711x_writeregs(sd, gm7113c_init);
break;
case SAA7113:
saa711x_writeregs(sd, saa7113_init);
if (pdata && pdata->saa7113_force_gm7113c_init)
saa711x_writeregs(sd, gm7113c_init);
else
saa711x_writeregs(sd, saa7113_init);
break;
default:
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
saa711x_writeregs(sd, saa7115_init_auto_input);
}
if (state->ident > SAA7111A)
if (state->ident > SAA7111A && state->ident != GM7113C)
saa711x_writeregs(sd, saa7115_init_misc);
if (pdata)
saa711x_write_platform_data(state, pdata);
saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
v4l2_ctrl_handler_setup(hdl);

View file

@ -201,6 +201,25 @@
#define R_FB_PULSE_C_POS_MSB 0xfb
#define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff
/* SAA7113 bit-masks */
#define SAA7113_R_08_HTC_OFFSET 3
#define SAA7113_R_08_HTC_MASK (0x3 << SAA7113_R_08_HTC_OFFSET)
#define SAA7113_R_08_FSEL 0x40
#define SAA7113_R_08_AUFD 0x80
#define SAA7113_R_10_VRLN_OFFSET 3
#define SAA7113_R_10_VRLN_MASK (0x1 << SAA7113_R_10_VRLN_OFFSET)
#define SAA7113_R_10_OFTS_OFFSET 6
#define SAA7113_R_10_OFTS_MASK (0x3 << SAA7113_R_10_OFTS_OFFSET)
#define SAA7113_R_12_RTS0_OFFSET 0
#define SAA7113_R_12_RTS0_MASK (0xf << SAA7113_R_12_RTS0_OFFSET)
#define SAA7113_R_12_RTS1_OFFSET 4
#define SAA7113_R_12_RTS1_MASK (0xf << SAA7113_R_12_RTS1_OFFSET)
#define SAA7113_R_13_ADLSB_OFFSET 7
#define SAA7113_R_13_ADLSB_MASK (0x1 << SAA7113_R_13_ADLSB_OFFSET)
#if 0
/* Those structs will be used in the future for debug purposes */
struct saa711x_reg_descr {

View file

@ -87,6 +87,17 @@ static void print_pll(struct device *dev, struct smiapp_pll *pll)
dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz);
}
/*
* Heuristically guess the PLL tree for a given common multiplier and
* divisor. Begin with the operational timing and continue to video
* timing once operational timing has been verified.
*
* @mul is the PLL multiplier and @div is the common divisor
* (pre_pll_clk_div and op_sys_clk_div combined). The final PLL
* multiplier will be a multiple of @mul.
*
* @return Zero on success, error code on error.
*/
static int __smiapp_pll_calculate(struct device *dev,
const struct smiapp_pll_limits *limits,
struct smiapp_pll *pll, uint32_t mul,
@ -95,6 +106,12 @@ static int __smiapp_pll_calculate(struct device *dev,
uint32_t sys_div;
uint32_t best_pix_div = INT_MAX >> 1;
uint32_t vt_op_binning_div;
/*
* Higher multipliers (and divisors) are often required than
* necessitated by the external clock and the output clocks.
* There are limits for all values in the clock tree. These
* are the minimum and maximum multiplier for mul.
*/
uint32_t more_mul_min, more_mul_max;
uint32_t more_mul_factor;
uint32_t min_vt_div, max_vt_div, vt_div;

View file

@ -1122,9 +1122,9 @@ static int smiapp_power_on(struct smiapp_sensor *sensor)
rval = sensor->platform_data->set_xclk(
&sensor->src->sd, sensor->platform_data->ext_clk);
else
rval = clk_enable(sensor->ext_clk);
rval = clk_prepare_enable(sensor->ext_clk);
if (rval < 0) {
dev_dbg(&client->dev, "failed to set xclk\n");
dev_dbg(&client->dev, "failed to enable xclk\n");
goto out_xclk_fail;
}
usleep_range(1000, 1000);
@ -1244,7 +1244,7 @@ out_cci_addr_fail:
if (sensor->platform_data->set_xclk)
sensor->platform_data->set_xclk(&sensor->src->sd, 0);
else
clk_disable(sensor->ext_clk);
clk_disable_unprepare(sensor->ext_clk);
out_xclk_fail:
regulator_disable(sensor->vana);
@ -1270,7 +1270,7 @@ static void smiapp_power_off(struct smiapp_sensor *sensor)
if (sensor->platform_data->set_xclk)
sensor->platform_data->set_xclk(&sensor->src->sd, 0);
else
clk_disable(sensor->ext_clk);
clk_disable_unprepare(sensor->ext_clk);
usleep_range(5000, 5000);
regulator_disable(sensor->vana);
sensor->streaming = 0;
@ -1835,12 +1835,12 @@ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev,
* sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]
/ sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE];
a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
a = clamp(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN],
sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX]);
b = clamp(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN],
sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX]);
max_m = clamp(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN],
sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX]);
dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m);
@ -2363,11 +2363,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
}
if (!sensor->platform_data->set_xclk) {
sensor->ext_clk = devm_clk_get(&client->dev,
sensor->platform_data->ext_clk_name);
sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk");
if (IS_ERR(sensor->ext_clk)) {
dev_err(&client->dev, "could not get clock %s\n",
sensor->platform_data->ext_clk_name);
dev_err(&client->dev, "could not get clock\n");
return -ENODEV;
}
@ -2375,8 +2373,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
sensor->platform_data->ext_clk);
if (rval < 0) {
dev_err(&client->dev,
"unable to set clock %s freq to %u\n",
sensor->platform_data->ext_clk_name,
"unable to set clock freq to %u\n",
sensor->platform_data->ext_clk);
return -ENODEV;
}
@ -2839,7 +2836,7 @@ static int smiapp_remove(struct i2c_client *client)
if (sensor->platform_data->set_xclk)
sensor->platform_data->set_xclk(&sensor->src->sd, 0);
else
clk_disable(sensor->ext_clk);
clk_disable_unprepare(sensor->ext_clk);
sensor->power_count = 0;
}

View file

@ -946,6 +946,10 @@ static int mt9m111_probe(struct i2c_client *client,
if (!mt9m111)
return -ENOMEM;
mt9m111->clk = v4l2_clk_get(&client->dev, "mclk");
if (IS_ERR(mt9m111->clk))
return -EPROBE_DEFER;
/* Default HIGHPOWER context */
mt9m111->ctx = &context_b;
@ -963,8 +967,10 @@ static int mt9m111_probe(struct i2c_client *client,
&mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
V4L2_EXPOSURE_AUTO);
mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
if (mt9m111->hdl.error)
return mt9m111->hdl.error;
if (mt9m111->hdl.error) {
ret = mt9m111->hdl.error;
goto out_clkput;
}
/* Second stage probe - when a capture adapter is there */
mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
@ -975,18 +981,25 @@ static int mt9m111_probe(struct i2c_client *client,
mt9m111->lastpage = -1;
mutex_init(&mt9m111->power_lock);
mt9m111->clk = v4l2_clk_get(&client->dev, "mclk");
if (IS_ERR(mt9m111->clk)) {
ret = PTR_ERR(mt9m111->clk);
goto eclkget;
}
ret = soc_camera_power_init(&client->dev, ssdd);
if (ret < 0)
goto out_hdlfree;
ret = mt9m111_video_probe(client);
if (ret) {
v4l2_clk_put(mt9m111->clk);
eclkget:
v4l2_ctrl_handler_free(&mt9m111->hdl);
}
if (ret < 0)
goto out_hdlfree;
mt9m111->subdev.dev = &client->dev;
ret = v4l2_async_register_subdev(&mt9m111->subdev);
if (ret < 0)
goto out_hdlfree;
return 0;
out_hdlfree:
v4l2_ctrl_handler_free(&mt9m111->hdl);
out_clkput:
v4l2_clk_put(mt9m111->clk);
return ret;
}
@ -995,6 +1008,7 @@ static int mt9m111_remove(struct i2c_client *client)
{
struct mt9m111 *mt9m111 = to_mt9m111(client);
v4l2_async_unregister_subdev(&mt9m111->subdev);
v4l2_clk_put(mt9m111->clk);
v4l2_device_unregister_subdev(&mt9m111->subdev);
v4l2_ctrl_handler_free(&mt9m111->hdl);

View file

@ -594,9 +594,12 @@ static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk);
if (ret < 0)
return ret;
vdev->dev.type = &mt9t031_dev_type;
if (vdev)
/* Not needed during probing, when vdev isn't available yet */
vdev->dev.type = &mt9t031_dev_type;
} else {
vdev->dev.type = NULL;
if (vdev)
vdev->dev.type = NULL;
soc_camera_power_off(&client->dev, ssdd, mt9t031->clk);
}

View file

@ -291,10 +291,8 @@ static int ths7303_log_status(struct v4l2_subdev *sd)
struct v4l2_bt_timings *bt = bt = &state->bt;
u32 frame_width, frame_height;
frame_width = bt->width + bt->hfrontporch +
bt->hsync + bt->hbackporch;
frame_height = bt->height + bt->vfrontporch +
bt->vsync + bt->vbackporch;
frame_width = V4L2_DV_BT_FRAME_WIDTH(bt);
frame_height = V4L2_DV_BT_FRAME_HEIGHT(bt);
v4l2_info(sd,
"timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. Polarities = 0x%x\n",
bt->width, bt->height, bt->interlaced ? "i" : "p",

View file

@ -21,6 +21,8 @@
#include <linux/module.h>
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include "ths8200_regs.h"
@ -42,18 +44,16 @@ struct ths8200_state {
struct v4l2_dv_timings dv_timings;
};
static const struct v4l2_dv_timings ths8200_timings[] = {
V4L2_DV_BT_CEA_720X480P59_94,
V4L2_DV_BT_CEA_1280X720P24,
V4L2_DV_BT_CEA_1280X720P25,
V4L2_DV_BT_CEA_1280X720P30,
V4L2_DV_BT_CEA_1280X720P50,
V4L2_DV_BT_CEA_1280X720P60,
V4L2_DV_BT_CEA_1920X1080P24,
V4L2_DV_BT_CEA_1920X1080P25,
V4L2_DV_BT_CEA_1920X1080P30,
V4L2_DV_BT_CEA_1920X1080P50,
V4L2_DV_BT_CEA_1920X1080P60,
static const struct v4l2_dv_timings_cap ths8200_timings_cap = {
.type = V4L2_DV_BT_656_1120,
.bt = {
.max_width = 1920,
.max_height = 1080,
.min_pixelclock = 25000000,
.max_pixelclock = 148500000,
.standards = V4L2_DV_BT_STD_CEA861,
.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE,
},
};
static inline struct ths8200_state *to_state(struct v4l2_subdev *sd)
@ -63,22 +63,22 @@ static inline struct ths8200_state *to_state(struct v4l2_subdev *sd)
static inline unsigned hblanking(const struct v4l2_bt_timings *t)
{
return t->hfrontporch + t->hsync + t->hbackporch;
return V4L2_DV_BT_BLANKING_WIDTH(t);
}
static inline unsigned htotal(const struct v4l2_bt_timings *t)
{
return t->width + t->hfrontporch + t->hsync + t->hbackporch;
return V4L2_DV_BT_FRAME_WIDTH(t);
}
static inline unsigned vblanking(const struct v4l2_bt_timings *t)
{
return t->vfrontporch + t->vsync + t->vbackporch;
return V4L2_DV_BT_BLANKING_HEIGHT(t);
}
static inline unsigned vtotal(const struct v4l2_bt_timings *t)
{
return t->height + t->vfrontporch + t->vsync + t->vbackporch;
return V4L2_DV_BT_FRAME_HEIGHT(t);
}
static int ths8200_read(struct v4l2_subdev *sd, u8 reg)
@ -133,39 +133,6 @@ static int ths8200_s_register(struct v4l2_subdev *sd,
}
#endif
static void ths8200_print_timings(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings,
const char *txt, bool detailed)
{
struct v4l2_bt_timings *bt = &timings->bt;
u32 htot, vtot;
if (timings->type != V4L2_DV_BT_656_1120)
return;
htot = htotal(bt);
vtot = vtotal(bt);
v4l2_info(sd, "%s %dx%d%s%d (%dx%d)",
txt, bt->width, bt->height, bt->interlaced ? "i" : "p",
(htot * vtot) > 0 ? ((u32)bt->pixelclock / (htot * vtot)) : 0,
htot, vtot);
if (detailed) {
v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n",
bt->hfrontporch,
(bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-",
bt->hsync, bt->hbackporch);
v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n",
bt->vfrontporch,
(bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-",
bt->vsync, bt->vbackporch);
v4l2_info(sd,
" pixelclock: %lld, flags: 0x%x, standards: 0x%x\n",
bt->pixelclock, bt->flags, bt->standards);
}
}
static int ths8200_log_status(struct v4l2_subdev *sd)
{
struct ths8200_state *state = to_state(sd);
@ -182,9 +149,8 @@ static int ths8200_log_status(struct v4l2_subdev *sd)
ths8200_read(sd, THS8200_DTG2_PIXEL_CNT_LSB),
(ths8200_read(sd, THS8200_DTG2_LINE_CNT_MSB) & 0x07) * 256 +
ths8200_read(sd, THS8200_DTG2_LINE_CNT_LSB));
ths8200_print_timings(sd, &state->dv_timings,
"Configured format:", true);
v4l2_print_dv_timings(sd->name, "Configured format:",
&state->dv_timings, true);
return 0;
}
@ -409,25 +375,15 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings)
{
struct ths8200_state *state = to_state(sd);
int i;
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
if (timings->type != V4L2_DV_BT_656_1120)
if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap,
NULL, NULL))
return -EINVAL;
/* TODO Support interlaced formats */
if (timings->bt.interlaced) {
v4l2_dbg(1, debug, sd, "TODO Support interlaced formats\n");
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(ths8200_timings); i++) {
if (v4l_match_dv_timings(&ths8200_timings[i], timings, 10))
break;
}
if (i == ARRAY_SIZE(ths8200_timings)) {
if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10,
NULL, NULL)) {
v4l2_dbg(1, debug, sd, "Unsupported format\n");
return -EINVAL;
}
@ -457,26 +413,14 @@ static int ths8200_g_dv_timings(struct v4l2_subdev *sd,
static int ths8200_enum_dv_timings(struct v4l2_subdev *sd,
struct v4l2_enum_dv_timings *timings)
{
/* Check requested format index is within range */
if (timings->index >= ARRAY_SIZE(ths8200_timings))
return -EINVAL;
timings->timings = ths8200_timings[timings->index];
return 0;
return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap,
NULL, NULL);
}
static int ths8200_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
cap->type = V4L2_DV_BT_656_1120;
cap->bt.max_width = 1920;
cap->bt.max_height = 1080;
cap->bt.min_pixelclock = 27000000;
cap->bt.max_pixelclock = 148500000;
cap->bt.standards = V4L2_DV_BT_STD_CEA861;
cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE;
*cap = ths8200_timings_cap;
return 0;
}
@ -500,6 +444,7 @@ static int ths8200_probe(struct i2c_client *client,
{
struct ths8200_state *state;
struct v4l2_subdev *sd;
int error;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@ -517,6 +462,10 @@ static int ths8200_probe(struct i2c_client *client,
ths8200_core_init(sd);
error = v4l2_async_register_subdev(&state->sd);
if (error)
return error;
v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
client->addr << 1, client->adapter->name);
@ -526,12 +475,13 @@ static int ths8200_probe(struct i2c_client *client,
static int ths8200_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ths8200_state *decoder = to_state(sd);
v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name,
client->addr << 1, client->adapter->name);
ths8200_s_power(sd, false);
v4l2_async_unregister_subdev(&decoder->sd);
v4l2_device_unregister_subdev(sd);
return 0;
@ -543,10 +493,19 @@ static struct i2c_device_id ths8200_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ths8200_id);
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id ths8200_of_match[] = {
{ .compatible = "ti,ths8200", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, ths8200_of_match);
#endif
static struct i2c_driver ths8200_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "ths8200",
.of_match_table = of_match_ptr(ths8200_of_match),
},
.probe = ths8200_probe,
.remove = ths8200_remove,

View file

@ -36,6 +36,7 @@
#include <linux/module.h>
#include <linux/v4l2-mediabus.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
#include <media/v4l2-mediabus.h>
@ -1175,16 +1176,22 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
sd->ctrl_handler = &decoder->hdl;
if (decoder->hdl.error) {
ret = decoder->hdl.error;
v4l2_ctrl_handler_free(&decoder->hdl);
return ret;
goto done;
}
v4l2_ctrl_handler_setup(&decoder->hdl);
v4l2_info(sd, "%s decoder driver registered !!\n", sd->name);
return 0;
ret = v4l2_async_register_subdev(&decoder->sd);
if (!ret)
v4l2_info(sd, "%s decoder driver registered !!\n", sd->name);
done:
if (ret < 0) {
v4l2_ctrl_handler_free(&decoder->hdl);
#if defined(CONFIG_MEDIA_CONTROLLER)
media_entity_cleanup(&decoder->sd.entity);
#endif
}
return ret;
}
/**
@ -1199,6 +1206,7 @@ static int tvp514x_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct tvp514x_decoder *decoder = to_decoder(sd);
v4l2_async_unregister_subdev(&decoder->sd);
v4l2_device_unregister_subdev(sd);
#if defined(CONFIG_MEDIA_CONTROLLER)
media_entity_cleanup(&decoder->sd.entity);

View file

@ -31,9 +31,12 @@
#include <linux/module.h>
#include <linux/v4l2-dv-timings.h>
#include <media/tvp7002.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-of.h>
#include "tvp7002_reg.h"
MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver");
@ -942,6 +945,48 @@ static const struct v4l2_subdev_ops tvp7002_ops = {
.pad = &tvp7002_pad_ops,
};
static struct tvp7002_config *
tvp7002_get_pdata(struct i2c_client *client)
{
struct v4l2_of_endpoint bus_cfg;
struct tvp7002_config *pdata;
struct device_node *endpoint;
unsigned int flags;
if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
return client->dev.platform_data;
endpoint = v4l2_of_get_next_endpoint(client->dev.of_node, NULL);
if (!endpoint)
return NULL;
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
goto done;
v4l2_of_parse_endpoint(endpoint, &bus_cfg);
flags = bus_cfg.bus.parallel.flags;
if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
pdata->hs_polarity = 1;
if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
pdata->vs_polarity = 1;
if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
pdata->clk_polarity = 1;
if (flags & V4L2_MBUS_FIELD_EVEN_HIGH)
pdata->fid_polarity = 1;
if (flags & V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH)
pdata->sog_polarity = 1;
done:
of_node_put(endpoint);
return pdata;
}
/*
* tvp7002_probe - Probe a TVP7002 device
* @c: ptr to i2c_client struct
@ -953,32 +998,32 @@ static const struct v4l2_subdev_ops tvp7002_ops = {
*/
static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)
{
struct tvp7002_config *pdata = tvp7002_get_pdata(c);
struct v4l2_subdev *sd;
struct tvp7002 *device;
struct v4l2_dv_timings timings;
int polarity_a;
int polarity_b;
u8 revision;
int error;
if (pdata == NULL) {
dev_err(&c->dev, "No platform data\n");
return -EINVAL;
}
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(c->adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return -EIO;
if (!c->dev.platform_data) {
v4l_err(c, "No platform data!!\n");
return -ENODEV;
}
device = devm_kzalloc(&c->dev, sizeof(struct tvp7002), GFP_KERNEL);
if (!device)
return -ENOMEM;
sd = &device->sd;
device->pdata = c->dev.platform_data;
device->pdata = pdata;
device->current_timings = tvp7002_timings;
/* Tell v4l2 the device is ready */
@ -1039,6 +1084,10 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)
}
v4l2_ctrl_handler_setup(&device->hdl);
error = v4l2_async_register_subdev(&device->sd);
if (error)
goto error;
return 0;
error:
@ -1063,6 +1112,7 @@ static int tvp7002_remove(struct i2c_client *c)
v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter"
"on address 0x%x\n", c->addr);
v4l2_async_unregister_subdev(&device->sd);
#if defined(CONFIG_MEDIA_CONTROLLER)
media_entity_cleanup(&device->sd.entity);
#endif
@ -1078,9 +1128,18 @@ static const struct i2c_device_id tvp7002_id[] = {
};
MODULE_DEVICE_TABLE(i2c, tvp7002_id);
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id tvp7002_of_match[] = {
{ .compatible = "ti,tvp7002", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, tvp7002_of_match);
#endif
/* I2C driver data */
static struct i2c_driver tvp7002_driver = {
.driver = {
.of_match_table = of_match_ptr(tvp7002_of_match),
.owner = THIS_MODULE,
.name = TVP7002_MODULE_NAME,
},

View file

@ -20,6 +20,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/bitmap.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <media/media-entity.h>
@ -121,7 +122,6 @@ static struct media_entity *stack_pop(struct media_entity_graph *graph)
return entity;
}
#define stack_peek(en) ((en)->stack[(en)->top - 1].entity)
#define link_top(en) ((en)->stack[(en)->top].link)
#define stack_top(en) ((en)->stack[(en)->top].entity)
@ -140,6 +140,12 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph,
{
graph->top = 0;
graph->stack[graph->top].entity = NULL;
bitmap_zero(graph->entities, MEDIA_ENTITY_ENUM_MAX_ID);
if (WARN_ON(entity->id >= MEDIA_ENTITY_ENUM_MAX_ID))
return;
__set_bit(entity->id, graph->entities);
stack_push(graph, entity);
}
EXPORT_SYMBOL_GPL(media_entity_graph_walk_start);
@ -180,9 +186,11 @@ media_entity_graph_walk_next(struct media_entity_graph *graph)
/* Get the entity in the other end of the link . */
next = media_entity_other(entity, link);
if (WARN_ON(next->id >= MEDIA_ENTITY_ENUM_MAX_ID))
return NULL;
/* Was it the entity we came here from? */
if (next == stack_peek(graph)) {
/* Has the entity already been visited? */
if (__test_and_set_bit(next->id, graph->entities)) {
link_top(graph)++;
continue;
}

View file

@ -4441,9 +4441,7 @@ static void tibetCS16_init(struct bttv *btv)
* is {3, 0, 2, 1}, i.e. the first controller to be detected is logical
* unit 3, the second (which is the master) is logical unit 0, etc.
* We need to maintain the status of the analog switch (which of the 16
* cameras is connected to which of the 4 controllers). Rather than
* add to the bttv structure for this, we use the data reserved for
* the mbox (unused for this card type).
* cameras is connected to which of the 4 controllers) in sw_status array.
*/
/*
@ -4478,7 +4476,6 @@ static void kodicom4400r_write(struct bttv *btv,
*/
static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input)
{
char *sw_status;
int xaddr, yaddr;
struct bttv *mctlr;
static unsigned char map[4] = {3, 0, 2, 1};
@ -4489,14 +4486,13 @@ static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input)
}
yaddr = (btv->c.nr - mctlr->c.nr + 1) & 3; /* the '&' is for safety */
yaddr = map[yaddr];
sw_status = (char *)(&mctlr->mbox_we);
xaddr = input & 0xf;
/* Check if the controller/camera pair has changed, else ignore */
if (sw_status[yaddr] != xaddr)
if (mctlr->sw_status[yaddr] != xaddr)
{
/* "open" the old switch, "close" the new one, save the new */
kodicom4400r_write(mctlr, sw_status[yaddr], yaddr, 0);
sw_status[yaddr] = xaddr;
kodicom4400r_write(mctlr, mctlr->sw_status[yaddr], yaddr, 0);
mctlr->sw_status[yaddr] = xaddr;
kodicom4400r_write(mctlr, xaddr, yaddr, 1);
}
}
@ -4509,7 +4505,6 @@ static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input)
*/
static void kodicom4400r_init(struct bttv *btv)
{
char *sw_status = (char *)(&btv->mbox_we);
int ix;
gpio_inout(0x0003ff, 0x0003ff);
@ -4517,7 +4512,7 @@ static void kodicom4400r_init(struct bttv *btv)
gpio_write(0);
/* Preset camera 0 to the 4 controllers */
for (ix = 0; ix < 4; ix++) {
sw_status[ix] = ix;
btv->sw_status[ix] = ix;
kodicom4400r_write(btv, ix, ix, 1);
}
/*
@ -4794,7 +4789,6 @@ static void gv800s_write(struct bttv *btv,
static void gv800s_muxsel(struct bttv *btv, unsigned int input)
{
struct bttv *mctlr;
char *sw_status;
int xaddr, yaddr;
static unsigned int map[4][4] = { { 0x0, 0x4, 0xa, 0x6 },
{ 0x1, 0x5, 0xb, 0x7 },
@ -4807,14 +4801,13 @@ static void gv800s_muxsel(struct bttv *btv, unsigned int input)
return;
}
yaddr = (btv->c.nr - mctlr->c.nr) & 3;
sw_status = (char *)(&mctlr->mbox_we);
xaddr = map[yaddr][input] & 0xf;
/* Check if the controller/camera pair has changed, ignore otherwise */
if (sw_status[yaddr] != xaddr) {
if (mctlr->sw_status[yaddr] != xaddr) {
/* disable the old switch, enable the new one and save status */
gv800s_write(mctlr, sw_status[yaddr], yaddr, 0);
sw_status[yaddr] = xaddr;
gv800s_write(mctlr, mctlr->sw_status[yaddr], yaddr, 0);
mctlr->sw_status[yaddr] = xaddr;
gv800s_write(mctlr, xaddr, yaddr, 1);
}
}
@ -4822,7 +4815,6 @@ static void gv800s_muxsel(struct bttv *btv, unsigned int input)
/* GeoVision GV-800(S) "master" chip init */
static void gv800s_init(struct bttv *btv)
{
char *sw_status = (char *)(&btv->mbox_we);
int ix;
gpio_inout(0xf107f, 0xf107f);
@ -4831,7 +4823,7 @@ static void gv800s_init(struct bttv *btv)
/* Preset camera 0 to the 4 controllers */
for (ix = 0; ix < 4; ix++) {
sw_status[ix] = ix;
btv->sw_status[ix] = ix;
gv800s_write(btv, ix, ix, 1);
}

View file

@ -459,6 +459,9 @@ struct bttv {
int mbox_iow;
int mbox_csel;
/* switch status for multi-controller cards */
char sw_status[4];
/* risc memory management data
- must acquire s_lock before changing these
- only the irq handler is supported to touch top + bottom + vcurr */

View file

@ -29,6 +29,7 @@ config VIDEO_CX23885
select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT
select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT
select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT

View file

@ -23,6 +23,7 @@
#include "cx23885.h"
#include "cx23885-av.h"
#include "cx23885-video.h"
void cx23885_av_work_handler(struct work_struct *work)
{
@ -32,5 +33,17 @@ void cx23885_av_work_handler(struct work_struct *work)
v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine,
PCI_MSK_AV_CORE, &handled);
/* Getting here with the interrupt not handled
then probbaly flatiron does have pending interrupts.
*/
if (!handled) {
/* clear left and right adc channel interrupt request flag */
cx23885_flatiron_write(dev, 0x1f,
cx23885_flatiron_read(dev, 0x1f) | 0x80);
cx23885_flatiron_write(dev, 0x23,
cx23885_flatiron_read(dev, 0x23) | 0x80);
}
cx23885_irq_enable(dev, PCI_MSK_AV_CORE);
}

View file

@ -528,11 +528,12 @@ struct cx23885_board cx23885_boards[] = {
} },
},
[CX23885_BOARD_MYGICA_X8507] = {
.name = "Mygica X8507",
.name = "Mygica X8502/X8507 ISDB-T",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.tuner_bus = 1,
.porta = CX23885_ANALOG_VIDEO,
.portb = CX23885_MPEG_DVB,
.input = {
{
.type = CX23885_VMUX_TELEVISION,
@ -1281,7 +1282,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
case CX23885_BOARD_MYGICA_X8507:
/* GPIO-0 (0)Analog / (1)Digital TV */
/* GPIO-1 reset XC5000 */
/* GPIO-2 reset LGS8GL5 / LGS8G75 */
/* GPIO-2 demod reset */
cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1);
cx23885_gpio_clear(dev, GPIO_1 | GPIO_2);
mdelay(100);
@ -1677,6 +1678,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
break;
case CX23885_BOARD_MYGICA_X8506:
case CX23885_BOARD_MAGICPRO_PROHDTVE2:
case CX23885_BOARD_MYGICA_X8507:
ts1->gen_ctrl_val = 0x5; /* Parallel */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;

View file

@ -1941,10 +1941,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) {
cx23885_irq_disable(dev, PCI_MSK_AV_CORE);
if (!schedule_work(&dev->cx25840_work))
printk(KERN_ERR "%s: failed to set up deferred work for"
" AV Core/IR interrupt. Interrupt is disabled"
" and won't be re-enabled\n", dev->name);
schedule_work(&dev->cx25840_work);
handled++;
}

View file

@ -69,6 +69,7 @@
#include "stb6100_cfg.h"
#include "tda10071.h"
#include "a8293.h"
#include "mb86a20s.h"
static unsigned int debug;
@ -119,8 +120,6 @@ static void dvb_buf_release(struct videobuf_queue *q,
cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
}
static int cx23885_dvb_set_frontend(struct dvb_frontend *fe);
static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open)
{
struct videobuf_dvb_frontends *f;
@ -135,12 +134,6 @@ static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open)
if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
/*
* FIXME: Improve this path to avoid calling the
* cx23885_dvb_set_frontend() every time it passes here.
*/
cx23885_dvb_set_frontend(fe->dvb.frontend);
}
static struct videobuf_queue_ops dvb_qops = {
@ -500,6 +493,15 @@ static struct xc5000_config mygica_x8506_xc5000_config = {
.if_khz = 5380,
};
static struct mb86a20s_config mygica_x8507_mb86a20s_config = {
.demod_address = 0x10,
};
static struct xc5000_config mygica_x8507_xc5000_config = {
.i2c_address = 0x61,
.if_khz = 4000,
};
static struct stv090x_config prof_8000_stv090x_config = {
.device = STV0903,
.demod_mode = STV090x_SINGLE,
@ -556,14 +558,27 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe)
}
break;
case CX23885_BOARD_MYGICA_X8506:
case CX23885_BOARD_MYGICA_X8507:
case CX23885_BOARD_MAGICPRO_PROHDTVE2:
/* Select Digital TV */
cx23885_gpio_set(dev, GPIO_0);
break;
}
/* Call the real set_frontend */
if (port->set_frontend)
return port->set_frontend(fe);
return 0;
}
static void cx23885_set_frontend_hook(struct cx23885_tsport *port,
struct dvb_frontend *fe)
{
port->set_frontend = fe->ops.set_frontend;
fe->ops.set_frontend = cx23885_dvb_set_frontend;
}
static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = {
.prod = LGS8GXX_PROD_LGS8G75,
.demod_address = 0x19,
@ -771,6 +786,8 @@ static int dvb_register(struct cx23885_tsport *port)
0x60, &dev->i2c_bus[1].i2c_adap,
&hauppauge_hvr127x_config);
}
if (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1275)
cx23885_set_frontend_hook(port, fe0->dvb.frontend);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
@ -1106,6 +1123,21 @@ static int dvb_register(struct cx23885_tsport *port)
&i2c_bus2->i2c_adap,
&mygica_x8506_xc5000_config);
}
cx23885_set_frontend_hook(port, fe0->dvb.frontend);
break;
case CX23885_BOARD_MYGICA_X8507:
i2c_bus = &dev->i2c_bus[0];
i2c_bus2 = &dev->i2c_bus[1];
fe0->dvb.frontend = dvb_attach(mb86a20s_attach,
&mygica_x8507_mb86a20s_config,
&i2c_bus->i2c_adap);
if (fe0->dvb.frontend != NULL) {
dvb_attach(xc5000_attach,
fe0->dvb.frontend,
&i2c_bus2->i2c_adap,
&mygica_x8507_xc5000_config);
}
cx23885_set_frontend_hook(port, fe0->dvb.frontend);
break;
case CX23885_BOARD_MAGICPRO_PROHDTVE2:
i2c_bus = &dev->i2c_bus[0];
@ -1119,6 +1151,7 @@ static int dvb_register(struct cx23885_tsport *port)
&i2c_bus2->i2c_adap,
&magicpro_prohdtve2_xc5000_config);
}
cx23885_set_frontend_hook(port, fe0->dvb.frontend);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1850:
i2c_bus = &dev->i2c_bus[0];
@ -1249,6 +1282,10 @@ static int dvb_register(struct cx23885_tsport *port)
fe0->dvb.frontend = dvb_attach(ds3000_attach,
&tevii_ds3000_config,
&i2c_bus->i2c_adap);
if (fe0->dvb.frontend != NULL) {
dvb_attach(ts2020_attach, fe0->dvb.frontend,
&tevii_ts2020_config, &i2c_bus->i2c_adap);
}
break;
case CX23885_BOARD_PROF_8000:
i2c_bus = &dev->i2c_bus[0];

View file

@ -32,6 +32,7 @@
#include <asm/div64.h>
#include "cx23885.h"
#include "cx23885-video.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include "cx23885-ioctl.h"
@ -417,7 +418,7 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
mutex_unlock(&dev->lock);
}
static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
{
/* 8 bit registers, 8 bit values */
u8 buf[] = { reg, data };
@ -428,7 +429,7 @@ static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
}
static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg)
u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg)
{
/* 8 bit registers, 8 bit values */
int ret;

View file

@ -0,0 +1,26 @@
/*
* Driver for the Conexant CX23885/7/8 PCIe bridge
*
* Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef _CX23885_VIDEO_H_
#define _CX23885_VIDEO_H_
int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data);
u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg);
#endif

View file

@ -320,6 +320,8 @@ struct cx23885_tsport {
/* Workaround for a temp dvb_frontend that the tuner can attached to */
struct dvb_frontend analog_fe;
int (*set_frontend)(struct dvb_frontend *fe);
};
struct cx23885_kernel_ir {

View file

@ -72,9 +72,9 @@ config VIDEO_CX88_DVB
To compile this driver as a module, choose M here: the
module will be called cx88-dvb.
config VIDEO_CX88_VP3054
tristate "VP-3054 Secondary I2C Bus Support"
default m
config VIDEO_CX88_ENABLE_VP3054
bool "VP-3054 Secondary I2C Bus Support"
default y
depends on VIDEO_CX88_DVB && DVB_MT352
---help---
This adds DVB-T support for cards based on the
@ -82,6 +82,11 @@ config VIDEO_CX88_VP3054
which also require support for the VP-3054
Secondary I2C bus, such at DNTV Live! DVB-T Pro.
config VIDEO_CX88_VP3054
tristate
depends on VIDEO_CX88_DVB && VIDEO_CX88_ENABLE_VP3054
default y
config VIDEO_CX88_MPEG
tristate
depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD

View file

@ -259,7 +259,7 @@ struct cx88_input {
};
enum cx88_audio_chip {
CX88_AUDIO_WM8775,
CX88_AUDIO_WM8775 = 1,
CX88_AUDIO_TVAUDIO,
};

View file

@ -203,13 +203,23 @@ config VIDEO_SAMSUNG_EXYNOS_GSC
config VIDEO_SH_VEU
tristate "SuperH VEU mem2mem video processing driver"
depends on VIDEO_DEV && VIDEO_V4L2 && GENERIC_HARDIRQS
depends on VIDEO_DEV && VIDEO_V4L2 && GENERIC_HARDIRQS && HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
Support for the Video Engine Unit (VEU) on SuperH and
SH-Mobile SoCs.
config VIDEO_RENESAS_VSP1
tristate "Renesas VSP1 Video Processing Engine"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
select VIDEOBUF2_DMA_CONTIG
---help---
This is a V4L2 driver for the Renesas VSP1 video processing engine.
To compile this driver as a module, choose M here: the module
will be called vsp1.
endif # V4L_MEM2MEM_DRIVERS
menuconfig V4L_TEST_DRIVERS

View file

@ -46,6 +46,8 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
obj-y += davinci/
obj-$(CONFIG_ARCH_OMAP) += omap/

View file

@ -388,13 +388,8 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
params.hdelay = bt->hsync + bt->hbackporch;
params.vdelay = bt->vsync + bt->vbackporch;
params.line = bt->hfrontporch + bt->hsync
+ bt->hbackporch + bt->width;
params.frame = bt->vfrontporch + bt->vsync
+ bt->vbackporch + bt->height;
if (bt->interlaced)
params.frame += bt->il_vfrontporch + bt->il_vsync
+ bt->il_vbackporch;
params.line = V4L2_DV_BT_FRAME_WIDTH(bt);
params.frame = V4L2_DV_BT_FRAME_HEIGHT(bt);
} else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities
& V4L2_IN_CAP_STD) {
params.hdelay = 0;

File diff suppressed because it is too large Load diff

View file

@ -43,14 +43,26 @@
#define CODA_STREAM_ENDIAN_SELECT (1 << 0)
#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110
#define CODA_IMAGE_ENDIAN_SELECT (1 << 0)
#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114
#define CODA_BIT_STREAM_END_FLAG (1 << 2)
#define CODA_BIT_DEC_SEQ_INIT_ESCAPE (1 << 0)
#define CODA_REG_BIT_TEMP_BUF_ADDR 0x118
#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x))
#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x))
#define CODA_REG_BIT_FRM_DIS_FLG(x) (0x150 + 4 * (x))
#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140
#define CODA7_REG_BIT_AXI_SRAM_USE 0x140
#define CODA7_USE_BIT_ENABLE (1 << 0)
#define CODA7_USE_HOST_ME_ENABLE (1 << 11)
#define CODA7_USE_HOST_OVL_ENABLE (1 << 10)
#define CODA7_USE_HOST_DBK_ENABLE (1 << 9)
#define CODA7_USE_HOST_IP_ENABLE (1 << 8)
#define CODA7_USE_HOST_BIT_ENABLE (1 << 7)
#define CODA7_USE_ME_ENABLE (1 << 4)
#define CODA7_USE_HOST_ME_ENABLE (1 << 11)
#define CODA7_USE_OVL_ENABLE (1 << 3)
#define CODA7_USE_DBK_ENABLE (1 << 2)
#define CODA7_USE_IP_ENABLE (1 << 1)
#define CODA7_USE_BIT_ENABLE (1 << 0)
#define CODA_REG_BIT_BUSY 0x160
#define CODA_REG_BIT_BUSY_FLAG 1
#define CODA_REG_BIT_RUN_COMMAND 0x164
@ -84,6 +96,15 @@
#define CODA_MODE_INVALID 0xffff
#define CODA_REG_BIT_INT_ENABLE 0x170
#define CODA_INT_INTERRUPT_ENABLE (1 << 3)
#define CODA_REG_BIT_INT_REASON 0x174
#define CODA7_REG_BIT_RUN_AUX_STD 0x178
#define CODA_MP4_AUX_MPEG4 0
#define CODA_MP4_AUX_DIVX3 1
#define CODA_VPX_AUX_THO 0
#define CODA_VPX_AUX_VP6 1
#define CODA_VPX_AUX_VP8 2
#define CODA_H264_AUX_AVC 0
#define CODA_H264_AUX_MVC 1
/*
* Commands' mailbox:
@ -92,15 +113,89 @@
* issued.
*/
/* Decoder Sequence Initialization */
#define CODA_CMD_DEC_SEQ_BB_START 0x180
#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184
#define CODA_CMD_DEC_SEQ_OPTION 0x188
#define CODA_REORDER_ENABLE (1 << 1)
#define CODADX6_QP_REPORT (1 << 0)
#define CODA7_MP4_DEBLK_ENABLE (1 << 0)
#define CODA_CMD_DEC_SEQ_SRC_SIZE 0x18c
#define CODA_CMD_DEC_SEQ_START_BYTE 0x190
#define CODA_CMD_DEC_SEQ_PS_BB_START 0x194
#define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198
#define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c
#define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c
#define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0
#define CODA7_RET_DEC_SEQ_ASPECT 0x1b0
#define CODA_RET_DEC_SEQ_SUCCESS 0x1c0
#define CODA_RET_DEC_SEQ_SRC_FMT 0x1c4 /* SRC_SIZE on CODA7 */
#define CODA_RET_DEC_SEQ_SRC_SIZE 0x1c4
#define CODA_RET_DEC_SEQ_SRC_F_RATE 0x1c8
#define CODA9_RET_DEC_SEQ_ASPECT 0x1c8
#define CODA_RET_DEC_SEQ_FRAME_NEED 0x1cc
#define CODA_RET_DEC_SEQ_FRAME_DELAY 0x1d0
#define CODA_RET_DEC_SEQ_INFO 0x1d4
#define CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT 0x1d8
#define CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM 0x1dc
#define CODA_RET_DEC_SEQ_NEXT_FRAME_NUM 0x1e0
#define CODA_RET_DEC_SEQ_ERR_REASON 0x1e0
#define CODA_RET_DEC_SEQ_FRATE_NR 0x1e4
#define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8
#define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4
#define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8
/* Decoder Picture Run */
#define CODA_CMD_DEC_PIC_ROT_MODE 0x180
#define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184
#define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188
#define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c
#define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190
#define CODA_CMD_DEC_PIC_OPTION 0x194
#define CODA_PRE_SCAN_EN (1 << 0)
#define CODA_PRE_SCAN_MODE_DECODE (0 << 1)
#define CODA_PRE_SCAN_MODE_RETURN (1 << 1)
#define CODA_IFRAME_SEARCH_EN (1 << 2)
#define CODA_SKIP_FRAME_MODE (0x3 << 3)
#define CODA_CMD_DEC_PIC_SKIP_NUM 0x198
#define CODA_CMD_DEC_PIC_CHUNK_SIZE 0x19c
#define CODA_CMD_DEC_PIC_BB_START 0x1a0
#define CODA_CMD_DEC_PIC_START_BYTE 0x1a4
#define CODA_RET_DEC_PIC_SIZE 0x1bc
#define CODA_RET_DEC_PIC_FRAME_NUM 0x1c0
#define CODA_RET_DEC_PIC_FRAME_IDX 0x1c4
#define CODA_RET_DEC_PIC_ERR_MB 0x1c8
#define CODA_RET_DEC_PIC_TYPE 0x1cc
#define CODA_PIC_TYPE_MASK 0x7
#define CODA_PIC_TYPE_MASK_VC1 0x3f
#define CODA9_PIC_TYPE_FIRST_MASK (0x7 << 3)
#define CODA9_PIC_TYPE_IDR_MASK (0x3 << 6)
#define CODA7_PIC_TYPE_H264_NPF_MASK (0x3 << 16)
#define CODA7_PIC_TYPE_INTERLACED (1 << 18)
#define CODA_RET_DEC_PIC_POST 0x1d0
#define CODA_RET_DEC_PIC_MVC_REPORT 0x1d0
#define CODA_RET_DEC_PIC_OPTION 0x1d4
#define CODA_RET_DEC_PIC_SUCCESS 0x1d8
#define CODA_RET_DEC_PIC_CUR_IDX 0x1dc
#define CODA_RET_DEC_PIC_CROP_LEFT_RIGHT 0x1e0
#define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4
#define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec
/* Encoder Sequence Initialization */
#define CODA_CMD_ENC_SEQ_BB_START 0x180
#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184
#define CODA_CMD_ENC_SEQ_OPTION 0x188
#define CODA7_OPTION_AVCINTRA16X16ONLY_OFFSET 9
#define CODA7_OPTION_GAMMA_OFFSET 8
#define CODA7_OPTION_RCQPMAX_OFFSET 7
#define CODADX6_OPTION_GAMMA_OFFSET 7
#define CODA7_OPTION_RCQPMIN_OFFSET 6
#define CODA_OPTION_LIMITQP_OFFSET 6
#define CODA_OPTION_RCINTRAQP_OFFSET 5
#define CODA_OPTION_FMO_OFFSET 4
#define CODA_OPTION_AVC_AUD_OFFSET 2
#define CODA_OPTION_SLICEREPORT_OFFSET 1
#define CODA_CMD_ENC_SEQ_COD_STD 0x18c
#define CODA_STD_MPEG4 0
@ -169,8 +264,10 @@
#define CODA_FMOPARAM_TYPE_MASK 1
#define CODA_FMOPARAM_SLICENUM_OFFSET 0
#define CODA_FMOPARAM_SLICENUM_MASK 0x0f
#define CODADX6_CMD_ENC_SEQ_INTRA_QP 0x1bc
#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8
#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc
#define CODA7_CMD_ENC_SEQ_INTRA_QP 0x1c4
#define CODA_CMD_ENC_SEQ_RC_QP_MAX 0x1c8
#define CODA_QPMAX_OFFSET 0
#define CODA_QPMAX_MASK 0x3f
@ -197,18 +294,24 @@
#define CODA_CMD_ENC_PIC_OPTION 0x194
#define CODA_CMD_ENC_PIC_BB_START 0x198
#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c
#define CODA_RET_ENC_FRAME_NUM 0x1c0
#define CODA_RET_ENC_PIC_TYPE 0x1c4
#define CODA_RET_ENC_PIC_FRAME_IDX 0x1c8
#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc
#define CODA_RET_ENC_PIC_FLAG 0x1d0
#define CODA_RET_ENC_PIC_SUCCESS 0x1d8
/* Set Frame Buffer */
#define CODA_CMD_SET_FRAME_BUF_NUM 0x180
#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184
#define CODA_CMD_SET_FRAME_SLICE_BB_START 0x188
#define CODA_CMD_SET_FRAME_SLICE_BB_SIZE 0x18c
#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190
#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194
#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198
#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c
#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0
#define CODA7_CMD_SET_FRAME_MAX_DEC_SIZE 0x1a4
#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8
/* Encoder Header */

View file

@ -1743,11 +1743,10 @@ static int vpbe_display_probe(struct platform_device *pdev)
printk(KERN_DEBUG "vpbe_display_probe\n");
/* Allocate memory for vpbe_display */
disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL);
if (!disp_dev) {
printk(KERN_ERR "ran out of memory\n");
disp_dev = devm_kzalloc(&pdev->dev, sizeof(struct vpbe_display),
GFP_KERNEL);
if (!disp_dev)
return -ENOMEM;
}
spin_lock_init(&disp_dev->dma_queue_lock);
/*
@ -1786,26 +1785,24 @@ static int vpbe_display_probe(struct platform_device *pdev)
}
irq = res->start;
if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER,
disp_dev)) {
err = devm_request_irq(&pdev->dev, irq, venc_isr, IRQF_DISABLED,
VPBE_DISPLAY_DRIVER, disp_dev);
if (err) {
v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
"Unable to request interrupt\n");
err = -ENODEV;
goto probe_out;
}
for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
err = -ENODEV;
goto probe_out_irq;
goto probe_out;
}
}
printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n");
return 0;
probe_out_irq:
free_irq(res->start, disp_dev);
probe_out:
for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
/* Get the pointer to the layer object */
@ -1817,7 +1814,6 @@ probe_out:
kfree(disp_dev->dev[k]);
}
}
kfree(disp_dev);
return err;
}
@ -1830,15 +1826,10 @@ static int vpbe_display_remove(struct platform_device *pdev)
struct vpbe_layer *vpbe_display_layer;
struct vpbe_display *disp_dev = platform_get_drvdata(pdev);
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
struct resource *res;
int i;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n");
/* unregister irq */
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
free_irq(res->start, disp_dev);
/* deinitialize the vpbe display controller */
if (NULL != vpbe_dev->ops.deinitialize)
vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev);

View file

@ -1547,61 +1547,36 @@ static int osd_probe(struct platform_device *pdev)
const struct platform_device_id *pdev_id;
struct osd_state *osd;
struct resource *res;
int ret = 0;
osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL);
pdev_id = platform_get_device_id(pdev);
if (!pdev_id)
return -EINVAL;
osd = devm_kzalloc(&pdev->dev, sizeof(struct osd_state), GFP_KERNEL);
if (osd == NULL)
return -ENOMEM;
pdev_id = platform_get_device_id(pdev);
if (!pdev_id) {
ret = -EINVAL;
goto free_mem;
}
osd->dev = &pdev->dev;
osd->vpbe_type = pdev_id->driver_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(osd->dev, "Unable to get OSD register address map\n");
ret = -ENODEV;
goto free_mem;
}
osd->osd_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(osd->osd_base))
return PTR_ERR(osd->osd_base);
osd->osd_base_phys = res->start;
osd->osd_size = resource_size(res);
if (!request_mem_region(osd->osd_base_phys, osd->osd_size,
MODULE_NAME)) {
dev_err(osd->dev, "Unable to reserve OSD MMIO region\n");
ret = -ENODEV;
goto free_mem;
}
osd->osd_base = ioremap_nocache(res->start, osd->osd_size);
if (!osd->osd_base) {
dev_err(osd->dev, "Unable to map the OSD region\n");
ret = -ENODEV;
goto release_mem_region;
}
spin_lock_init(&osd->lock);
osd->ops = osd_ops;
platform_set_drvdata(pdev, osd);
dev_notice(osd->dev, "OSD sub device probe success\n");
return ret;
release_mem_region:
release_mem_region(osd->osd_base_phys, osd->osd_size);
free_mem:
kfree(osd);
return ret;
return 0;
}
static int osd_remove(struct platform_device *pdev)
{
struct osd_state *osd = platform_get_drvdata(pdev);
iounmap((void *)osd->osd_base);
release_mem_region(osd->osd_base_phys, osd->osd_size);
kfree(osd);
return 0;
}

View file

@ -639,105 +639,46 @@ static int venc_probe(struct platform_device *pdev)
const struct platform_device_id *pdev_id;
struct venc_state *venc;
struct resource *res;
int ret;
venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL);
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "No platform data for VENC sub device");
return -EINVAL;
}
pdev_id = platform_get_device_id(pdev);
if (!pdev_id)
return -EINVAL;
venc = devm_kzalloc(&pdev->dev, sizeof(struct venc_state), GFP_KERNEL);
if (venc == NULL)
return -ENOMEM;
pdev_id = platform_get_device_id(pdev);
if (!pdev_id) {
ret = -EINVAL;
goto free_mem;
}
venc->venc_type = pdev_id->driver_data;
venc->pdev = &pdev->dev;
venc->pdata = pdev->dev.platform_data;
if (NULL == venc->pdata) {
dev_err(venc->pdev, "Unable to get platform data for"
" VENC sub device");
ret = -ENOENT;
goto free_mem;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(venc->pdev,
"Unable to get VENC register address map\n");
ret = -ENODEV;
goto free_mem;
}
if (!request_mem_region(res->start, resource_size(res), "venc")) {
dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n");
ret = -ENODEV;
goto free_mem;
}
venc->venc_base = ioremap_nocache(res->start, resource_size(res));
if (!venc->venc_base) {
dev_err(venc->pdev, "Unable to map VENC IO space\n");
ret = -ENODEV;
goto release_venc_mem_region;
}
venc->venc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(venc->venc_base))
return PTR_ERR(venc->venc_base);
if (venc->venc_type != VPBE_VERSION_1) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(venc->pdev,
"Unable to get VDAC_CONFIG address map\n");
ret = -ENODEV;
goto unmap_venc_io;
}
if (!request_mem_region(res->start,
resource_size(res), "venc")) {
dev_err(venc->pdev,
"Unable to reserve VDAC_CONFIG MMIO region\n");
ret = -ENODEV;
goto unmap_venc_io;
}
venc->vdaccfg_reg = ioremap_nocache(res->start,
resource_size(res));
if (!venc->vdaccfg_reg) {
dev_err(venc->pdev,
"Unable to map VDAC_CONFIG IO space\n");
ret = -ENODEV;
goto release_vdaccfg_mem_region;
}
venc->vdaccfg_reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(venc->vdaccfg_reg))
return PTR_ERR(venc->vdaccfg_reg);
}
spin_lock_init(&venc->lock);
platform_set_drvdata(pdev, venc);
dev_notice(venc->pdev, "VENC sub device probe success\n");
return 0;
release_vdaccfg_mem_region:
release_mem_region(res->start, resource_size(res));
unmap_venc_io:
iounmap(venc->venc_base);
release_venc_mem_region:
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
free_mem:
kfree(venc);
return ret;
return 0;
}
static int venc_remove(struct platform_device *pdev)
{
struct venc_state *venc = platform_get_drvdata(pdev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap((void *)venc->venc_base);
release_mem_region(res->start, resource_size(res));
if (venc->venc_type != VPBE_VERSION_1) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
iounmap((void *)venc->vdaccfg_reg);
release_mem_region(res->start, resource_size(res));
}
kfree(venc);
return 0;
}

View file

@ -1799,19 +1799,15 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
/* Configure video port timings */
std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
bt->hsync - 8;
std_info->eav2sav = V4L2_DV_BT_BLANKING_WIDTH(bt) - 8;
std_info->sav2eav = bt->width;
std_info->l1 = 1;
std_info->l3 = bt->vsync + bt->vbackporch + 1;
std_info->vsize = V4L2_DV_BT_FRAME_HEIGHT(bt);
if (bt->interlaced) {
if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
std_info->vsize = bt->height * 2 +
bt->vfrontporch + bt->vsync + bt->vbackporch +
bt->il_vfrontporch + bt->il_vsync +
bt->il_vbackporch;
std_info->l5 = std_info->vsize/2 -
(bt->vfrontporch - 1);
std_info->l7 = std_info->vsize/2 + 1;
@ -1825,8 +1821,6 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
return -EINVAL;
}
} else {
std_info->vsize = bt->height + bt->vfrontporch +
bt->vsync + bt->vbackporch;
std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
}
strncpy(std_info->name, "Custom timings BT656/1120", VPIF_MAX_NAME);
@ -1979,6 +1973,76 @@ vpif_init_free_channel_objects:
return err;
}
static int vpif_async_bound(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
{
int i;
for (i = 0; i < vpif_obj.config->subdev_count; i++)
if (!strcmp(vpif_obj.config->subdev_info[i].name,
subdev->name)) {
vpif_obj.sd[i] = subdev;
return 0;
}
return -EINVAL;
}
static int vpif_probe_complete(void)
{
struct common_obj *common;
struct channel_obj *ch;
int i, j, err, k;
for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
ch = vpif_obj.dev[j];
ch->channel_id = j;
common = &(ch->common[VPIF_VIDEO_INDEX]);
spin_lock_init(&common->irqlock);
mutex_init(&common->lock);
ch->video_dev->lock = &common->lock;
/* Initialize prio member of channel object */
v4l2_prio_init(&ch->prio);
video_set_drvdata(ch->video_dev, ch);
/* select input 0 */
err = vpif_set_input(vpif_obj.config, ch, 0);
if (err)
goto probe_out;
err = video_register_device(ch->video_dev,
VFL_TYPE_GRABBER, (j ? 1 : 0));
if (err)
goto probe_out;
}
v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
return 0;
probe_out:
for (k = 0; k < j; k++) {
/* Get the pointer to the channel object */
ch = vpif_obj.dev[k];
/* Unregister video device */
video_unregister_device(ch->video_dev);
}
kfree(vpif_obj.sd);
for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
ch = vpif_obj.dev[i];
/* Note: does nothing if ch->video_dev == NULL */
video_device_release(ch->video_dev);
}
v4l2_device_unregister(&vpif_obj.v4l2_dev);
return err;
}
static int vpif_async_complete(struct v4l2_async_notifier *notifier)
{
return vpif_probe_complete();
}
/**
* vpif_probe : This function probes the vpif capture driver
* @pdev: platform device pointer
@ -1989,12 +2053,10 @@ vpif_init_free_channel_objects:
static __init int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
struct vpif_capture_config *config;
int i, j, k, err;
int i, j, err;
int res_idx = 0;
struct i2c_adapter *i2c_adap;
struct channel_obj *ch;
struct common_obj *common;
struct video_device *vfd;
struct resource *res;
int subdev_count;
@ -2068,10 +2130,9 @@ static __init int vpif_probe(struct platform_device *pdev)
}
}
i2c_adap = i2c_get_adapter(1);
config = pdev->dev.platform_data;
vpif_obj.config = pdev->dev.platform_data;
subdev_count = config->subdev_count;
subdev_count = vpif_obj.config->subdev_count;
vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
GFP_KERNEL);
if (vpif_obj.sd == NULL) {
@ -2080,54 +2141,43 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_sd_error;
}
for (i = 0; i < subdev_count; i++) {
subdevdata = &config->subdev_info[i];
vpif_obj.sd[i] =
v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
i2c_adap,
&subdevdata->board_info,
NULL);
if (!vpif_obj.config->asd_sizes) {
i2c_adap = i2c_get_adapter(1);
for (i = 0; i < subdev_count; i++) {
subdevdata = &vpif_obj.config->subdev_info[i];
vpif_obj.sd[i] =
v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
i2c_adap,
&subdevdata->
board_info,
NULL);
if (!vpif_obj.sd[i]) {
vpif_err("Error registering v4l2 subdevice\n");
err = -ENODEV;
if (!vpif_obj.sd[i]) {
vpif_err("Error registering v4l2 subdevice\n");
err = -ENOMEM;
goto probe_subdev_out;
}
v4l2_info(&vpif_obj.v4l2_dev,
"registered sub device %s\n",
subdevdata->name);
}
vpif_probe_complete();
} else {
vpif_obj.notifier.subdevs = vpif_obj.config->asd;
vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0];
vpif_obj.notifier.bound = vpif_async_bound;
vpif_obj.notifier.complete = vpif_async_complete;
err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev,
&vpif_obj.notifier);
if (err) {
vpif_err("Error registering async notifier\n");
err = -EINVAL;
goto probe_subdev_out;
}
v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n",
subdevdata->name);
}
for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
ch = vpif_obj.dev[j];
ch->channel_id = j;
common = &(ch->common[VPIF_VIDEO_INDEX]);
spin_lock_init(&common->irqlock);
mutex_init(&common->lock);
ch->video_dev->lock = &common->lock;
/* Initialize prio member of channel object */
v4l2_prio_init(&ch->prio);
video_set_drvdata(ch->video_dev, ch);
/* select input 0 */
err = vpif_set_input(config, ch, 0);
if (err)
goto probe_out;
err = video_register_device(ch->video_dev,
VFL_TYPE_GRABBER, (j ? 1 : 0));
if (err)
goto probe_out;
}
v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
return 0;
probe_out:
for (k = 0; k < j; k++) {
/* Get the pointer to the channel object */
ch = vpif_obj.dev[k];
/* Unregister video device */
video_unregister_device(ch->video_dev);
}
probe_subdev_out:
/* free sub devices memory */
kfree(vpif_obj.sd);

View file

@ -142,6 +142,8 @@ struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS];
struct v4l2_subdev **sd;
struct v4l2_async_notifier notifier;
struct vpif_capture_config *config;
};
struct vpif_config_params {

View file

@ -1436,19 +1436,15 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
/* Configure video port timings */
std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
bt->hsync - 8;
std_info->eav2sav = V4L2_DV_BT_BLANKING_WIDTH(bt) - 8;
std_info->sav2eav = bt->width;
std_info->l1 = 1;
std_info->l3 = bt->vsync + bt->vbackporch + 1;
std_info->vsize = V4L2_DV_BT_FRAME_HEIGHT(bt);
if (bt->interlaced) {
if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
std_info->vsize = bt->height * 2 +
bt->vfrontporch + bt->vsync + bt->vbackporch +
bt->il_vfrontporch + bt->il_vsync +
bt->il_vbackporch;
std_info->l5 = std_info->vsize/2 -
(bt->vfrontporch - 1);
std_info->l7 = std_info->vsize/2 + 1;
@ -1462,8 +1458,6 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
return -EINVAL;
}
} else {
std_info->vsize = bt->height + bt->vfrontporch +
bt->vsync + bt->vbackporch;
std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
}
strncpy(std_info->name, "Custom timings BT656/1120",
@ -1618,6 +1612,102 @@ vpif_init_free_channel_objects:
return err;
}
static int vpif_async_bound(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
{
int i;
for (i = 0; i < vpif_obj.config->subdev_count; i++)
if (!strcmp(vpif_obj.config->subdevinfo[i].name,
subdev->name)) {
vpif_obj.sd[i] = subdev;
vpif_obj.sd[i]->grp_id = 1 << i;
return 0;
}
return -EINVAL;
}
static int vpif_probe_complete(void)
{
struct common_obj *common;
struct channel_obj *ch;
int j, err, k;
for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
ch = vpif_obj.dev[j];
/* Initialize field of the channel objects */
atomic_set(&ch->usrs, 0);
for (k = 0; k < VPIF_NUMOBJECTS; k++) {
ch->common[k].numbuffers = 0;
common = &ch->common[k];
common->io_usrs = 0;
common->started = 0;
spin_lock_init(&common->irqlock);
mutex_init(&common->lock);
common->numbuffers = 0;
common->set_addr = NULL;
common->ytop_off = 0;
common->ybtm_off = 0;
common->ctop_off = 0;
common->cbtm_off = 0;
common->cur_frm = NULL;
common->next_frm = NULL;
memset(&common->fmt, 0, sizeof(common->fmt));
common->numbuffers = config_params.numbuffers[k];
}
ch->initialized = 0;
if (vpif_obj.config->subdev_count)
ch->sd = vpif_obj.sd[0];
ch->channel_id = j;
if (j < 2)
ch->common[VPIF_VIDEO_INDEX].numbuffers =
config_params.numbuffers[ch->channel_id];
else
ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;
memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
/* Initialize prio member of channel object */
v4l2_prio_init(&ch->prio);
ch->common[VPIF_VIDEO_INDEX].fmt.type =
V4L2_BUF_TYPE_VIDEO_OUTPUT;
ch->video_dev->lock = &common->lock;
video_set_drvdata(ch->video_dev, ch);
/* select output 0 */
err = vpif_set_output(vpif_obj.config, ch, 0);
if (err)
goto probe_out;
/* register video device */
vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
(int)ch, (int)&ch->video_dev);
err = video_register_device(ch->video_dev,
VFL_TYPE_GRABBER, (j ? 3 : 2));
if (err < 0)
goto probe_out;
}
return 0;
probe_out:
for (k = 0; k < j; k++) {
ch = vpif_obj.dev[k];
video_unregister_device(ch->video_dev);
video_device_release(ch->video_dev);
ch->video_dev = NULL;
}
return err;
}
static int vpif_async_complete(struct v4l2_async_notifier *notifier)
{
return vpif_probe_complete();
}
/*
* vpif_probe: This function creates device entries by register itself to the
* V4L2 driver and initializes fields of each channel objects
@ -1625,11 +1715,9 @@ vpif_init_free_channel_objects:
static __init int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
struct vpif_display_config *config;
int i, j = 0, k, err = 0;
int i, j = 0, err = 0;
int res_idx = 0;
struct i2c_adapter *i2c_adap;
struct common_obj *common;
struct channel_obj *ch;
struct video_device *vfd;
struct resource *res;
@ -1708,11 +1796,9 @@ static __init int vpif_probe(struct platform_device *pdev)
size/2;
}
}
i2c_adap = i2c_get_adapter(1);
config = pdev->dev.platform_data;
subdev_count = config->subdev_count;
subdevdata = config->subdevinfo;
vpif_obj.config = pdev->dev.platform_data;
subdev_count = vpif_obj.config->subdev_count;
subdevdata = vpif_obj.config->subdevinfo;
vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
GFP_KERNEL);
if (vpif_obj.sd == NULL) {
@ -1721,86 +1807,41 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_sd_error;
}
for (i = 0; i < subdev_count; i++) {
vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
i2c_adap,
&subdevdata[i].board_info,
NULL);
if (!vpif_obj.sd[i]) {
vpif_err("Error registering v4l2 subdevice\n");
err = -ENODEV;
if (!vpif_obj.config->asd_sizes) {
i2c_adap = i2c_get_adapter(1);
for (i = 0; i < subdev_count; i++) {
vpif_obj.sd[i] =
v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
i2c_adap,
&subdevdata[i].
board_info,
NULL);
if (!vpif_obj.sd[i]) {
vpif_err("Error registering v4l2 subdevice\n");
err = -ENODEV;
goto probe_subdev_out;
}
if (vpif_obj.sd[i])
vpif_obj.sd[i]->grp_id = 1 << i;
}
vpif_probe_complete();
} else {
vpif_obj.notifier.subdevs = vpif_obj.config->asd;
vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0];
vpif_obj.notifier.bound = vpif_async_bound;
vpif_obj.notifier.complete = vpif_async_complete;
err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev,
&vpif_obj.notifier);
if (err) {
vpif_err("Error registering async notifier\n");
err = -EINVAL;
goto probe_subdev_out;
}
if (vpif_obj.sd[i])
vpif_obj.sd[i]->grp_id = 1 << i;
}
for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
ch = vpif_obj.dev[j];
/* Initialize field of the channel objects */
atomic_set(&ch->usrs, 0);
for (k = 0; k < VPIF_NUMOBJECTS; k++) {
ch->common[k].numbuffers = 0;
common = &ch->common[k];
common->io_usrs = 0;
common->started = 0;
spin_lock_init(&common->irqlock);
mutex_init(&common->lock);
common->numbuffers = 0;
common->set_addr = NULL;
common->ytop_off = common->ybtm_off = 0;
common->ctop_off = common->cbtm_off = 0;
common->cur_frm = common->next_frm = NULL;
memset(&common->fmt, 0, sizeof(common->fmt));
common->numbuffers = config_params.numbuffers[k];
}
ch->initialized = 0;
if (subdev_count)
ch->sd = vpif_obj.sd[0];
ch->channel_id = j;
if (j < 2)
ch->common[VPIF_VIDEO_INDEX].numbuffers =
config_params.numbuffers[ch->channel_id];
else
ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;
memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
/* Initialize prio member of channel object */
v4l2_prio_init(&ch->prio);
ch->common[VPIF_VIDEO_INDEX].fmt.type =
V4L2_BUF_TYPE_VIDEO_OUTPUT;
ch->video_dev->lock = &common->lock;
video_set_drvdata(ch->video_dev, ch);
/* select output 0 */
err = vpif_set_output(config, ch, 0);
if (err)
goto probe_out;
/* register video device */
vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
(int)ch, (int)&ch->video_dev);
err = video_register_device(ch->video_dev,
VFL_TYPE_GRABBER, (j ? 3 : 2));
if (err < 0)
goto probe_out;
}
v4l2_info(&vpif_obj.v4l2_dev,
" VPIF display driver initialized\n");
return 0;
probe_out:
for (k = 0; k < j; k++) {
ch = vpif_obj.dev[k];
video_unregister_device(ch->video_dev);
video_device_release(ch->video_dev);
ch->video_dev = NULL;
}
probe_subdev_out:
kfree(vpif_obj.sd);
vpif_sd_error:

View file

@ -148,7 +148,8 @@ struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS];
struct v4l2_subdev **sd;
struct v4l2_async_notifier notifier;
struct vpif_display_config *config;
};
struct vpif_config_params {

View file

@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/err.h>
#include <media/davinci/vpss.h>
@ -404,9 +405,8 @@ EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size);
static int vpss_probe(struct platform_device *pdev)
{
struct resource *r1, *r2;
struct resource *res;
char *platform_name;
int status;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data\n");
@ -427,38 +427,19 @@ static int vpss_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r1)
return -ENOENT;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
r1 = request_mem_region(r1->start, resource_size(r1), r1->name);
if (!r1)
return -EBUSY;
oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1));
if (!oper_cfg.vpss_regs_base0) {
status = -EBUSY;
goto fail1;
}
oper_cfg.vpss_regs_base0 = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(oper_cfg.vpss_regs_base0))
return PTR_ERR(oper_cfg.vpss_regs_base0);
if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!r2) {
status = -ENOENT;
goto fail2;
}
r2 = request_mem_region(r2->start, resource_size(r2), r2->name);
if (!r2) {
status = -EBUSY;
goto fail2;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
oper_cfg.vpss_regs_base1 = ioremap(r2->start,
resource_size(r2));
if (!oper_cfg.vpss_regs_base1) {
status = -EBUSY;
goto fail3;
}
oper_cfg.vpss_regs_base1 = devm_ioremap_resource(&pdev->dev,
res);
if (IS_ERR(oper_cfg.vpss_regs_base1))
return PTR_ERR(oper_cfg.vpss_regs_base1);
}
if (oper_cfg.platform == DM355) {
@ -493,30 +474,13 @@ static int vpss_probe(struct platform_device *pdev)
spin_lock_init(&oper_cfg.vpss_lock);
dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
return 0;
fail3:
release_mem_region(r2->start, resource_size(r2));
fail2:
iounmap(oper_cfg.vpss_regs_base0);
fail1:
release_mem_region(r1->start, resource_size(r1));
return status;
return 0;
}
static int vpss_remove(struct platform_device *pdev)
{
struct resource *res;
pm_runtime_disable(&pdev->dev);
iounmap(oper_cfg.vpss_regs_base0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
iounmap(oper_cfg.vpss_regs_base1);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
release_mem_region(res->start, resource_size(res));
}
return 0;
}

View file

@ -1122,10 +1122,14 @@ static int gsc_probe(struct platform_device *pdev)
goto err_clk;
}
ret = gsc_register_m2m_device(gsc);
ret = v4l2_device_register(dev, &gsc->v4l2_dev);
if (ret)
goto err_clk;
ret = gsc_register_m2m_device(gsc);
if (ret)
goto err_v4l2;
platform_set_drvdata(pdev, gsc);
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(&pdev->dev);
@ -1147,6 +1151,8 @@ err_pm:
pm_runtime_put(dev);
err_m2m:
gsc_unregister_m2m_device(gsc);
err_v4l2:
v4l2_device_unregister(&gsc->v4l2_dev);
err_clk:
gsc_clk_put(gsc);
return ret;
@ -1157,6 +1163,7 @@ static int gsc_remove(struct platform_device *pdev)
struct gsc_dev *gsc = platform_get_drvdata(pdev);
gsc_unregister_m2m_device(gsc);
v4l2_device_unregister(&gsc->v4l2_dev);
vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
pm_runtime_disable(&pdev->dev);
@ -1210,12 +1217,12 @@ static int gsc_resume(struct device *dev)
spin_unlock_irqrestore(&gsc->slock, flags);
return 0;
}
gsc_hw_set_sw_reset(gsc);
gsc_wait_reset(gsc);
spin_unlock_irqrestore(&gsc->slock, flags);
return gsc_m2m_resume(gsc);
if (!pm_runtime_suspended(dev))
return gsc_runtime_resume(dev);
return 0;
}
static int gsc_suspend(struct device *dev)
@ -1227,7 +1234,10 @@ static int gsc_suspend(struct device *dev)
if (test_and_set_bit(ST_SUSPEND, &gsc->state))
return 0;
return gsc_m2m_suspend(gsc);
if (!pm_runtime_suspended(dev))
return gsc_runtime_suspend(dev);
return 0;
}
static const struct dev_pm_ops gsc_pm_ops = {

View file

@ -343,6 +343,7 @@ struct gsc_dev {
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
struct video_device vdev;
struct v4l2_device v4l2_dev;
};
/**

View file

@ -751,6 +751,7 @@ int gsc_register_m2m_device(struct gsc_dev *gsc)
gsc->vdev.release = video_device_release_empty;
gsc->vdev.lock = &gsc->lock;
gsc->vdev.vfl_dir = VFL_DIR_M2M;
gsc->vdev.v4l2_dev = &gsc->v4l2_dev;
snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
GSC_MODULE_NAME, gsc->id);

View file

@ -1110,6 +1110,8 @@ static int fimc_remove(struct platform_device *pdev)
struct fimc_dev *fimc = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
clk_disable(fimc->clock[CLK_GATE]);
pm_runtime_set_suspended(&pdev->dev);
fimc_unregister_capture_subdev(fimc);

View file

@ -81,21 +81,46 @@ static int fimc_is_i2c_remove(struct platform_device *pdev)
return 0;
}
static int fimc_is_i2c_suspend(struct device *dev)
#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_PM_SLEEP)
static int fimc_is_i2c_runtime_suspend(struct device *dev)
{
struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev);
clk_disable_unprepare(isp_i2c->clock);
return 0;
}
static int fimc_is_i2c_resume(struct device *dev)
static int fimc_is_i2c_runtime_resume(struct device *dev)
{
struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev);
return clk_prepare_enable(isp_i2c->clock);
}
#endif
static UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend,
fimc_is_i2c_resume, NULL);
#ifdef CONFIG_PM_SLEEP
static int fimc_is_i2c_suspend(struct device *dev)
{
if (pm_runtime_suspended(dev))
return 0;
return fimc_is_i2c_runtime_suspend(dev);
}
static int fimc_is_i2c_resume(struct device *dev)
{
if (pm_runtime_suspended(dev))
return 0;
return fimc_is_i2c_runtime_resume(dev);
}
#endif
static struct dev_pm_ops fimc_is_i2c_pm_ops = {
SET_RUNTIME_PM_OPS(fimc_is_i2c_runtime_suspend,
fimc_is_i2c_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(fimc_is_i2c_suspend, fimc_is_i2c_resume)
};
static const struct of_device_id fimc_is_i2c_of_match[] = {
{ .compatible = FIMC_IS_I2C_COMPATIBLE },

View file

@ -56,7 +56,7 @@ static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)
__hw_param_copy(dst, src);
}
int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
static int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
{
struct is_param_region *par = &is->is_p_region->parameter;
struct chain_config *cfg = &is->config[is->config_index];
@ -287,7 +287,7 @@ void __is_set_sensor(struct fimc_is *is, int fps)
fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT);
}
void __is_set_init_isp_aa(struct fimc_is *is)
static void __maybe_unused __is_set_init_isp_aa(struct fimc_is *is)
{
struct isp_param *isp;

View file

@ -96,7 +96,7 @@ int fimc_is_hw_set_param(struct fimc_is *is)
return 0;
}
int fimc_is_hw_set_tune(struct fimc_is *is)
static int __maybe_unused fimc_is_hw_set_tune(struct fimc_is *is)
{
fimc_is_hw_wait_intmsr0_intmsd0(is);
@ -236,7 +236,7 @@ int fimc_is_itf_mode_change(struct fimc_is *is)
fimc_is_hw_change_mode(is);
ret = fimc_is_wait_event(is, IS_ST_CHANGE_MODE, 1,
FIMC_IS_CONFIG_TIMEOUT);
if (!ret < 0)
if (ret < 0)
dev_err(&is->pdev->dev, "%s(): mode change (%d) timeout\n",
__func__, is->config_index);
return ret;

View file

@ -993,3 +993,4 @@ module_exit(fimc_is_module_exit);
MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME);
MODULE_AUTHOR("Younghwan Joo <[email protected]>");
MODULE_AUTHOR("Sylwester Nawrocki <[email protected]>");
MODULE_LICENSE("GPL v2");

View file

@ -672,6 +672,8 @@ int fimc_isp_subdev_create(struct fimc_isp *isp)
mutex_init(&isp->subdev_lock);
v4l2_subdev_init(sd, &fimc_is_subdev_ops);
sd->owner = THIS_MODULE;
sd->grp_id = GRP_ID_FIMC_IS;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP");

View file

@ -90,7 +90,7 @@ static const struct fimc_fmt fimc_lite_formats[] = {
.name = "RAW10 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG10,
.colorspace = V4L2_COLORSPACE_SRGB,
.depth = { 10 },
.depth = { 16 },
.color = FIMC_FMT_RAW10,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
@ -99,7 +99,7 @@ static const struct fimc_fmt fimc_lite_formats[] = {
.name = "RAW12 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG12,
.colorspace = V4L2_COLORSPACE_SRGB,
.depth = { 12 },
.depth = { 16 },
.color = FIMC_FMT_RAW12,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
@ -1504,16 +1504,17 @@ static int fimc_lite_probe(struct platform_device *pdev)
struct resource *res;
int ret;
if (!dev->of_node)
return -ENODEV;
fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL);
if (!fimc)
return -ENOMEM;
if (dev->of_node) {
of_id = of_match_node(flite_of_match, dev->of_node);
if (of_id)
drv_data = (struct flite_drvdata *)of_id->data;
fimc->index = of_alias_get_id(dev->of_node, "fimc-lite");
}
of_id = of_match_node(flite_of_match, dev->of_node);
if (of_id)
drv_data = (struct flite_drvdata *)of_id->data;
fimc->index = of_alias_get_id(dev->of_node, "fimc-lite");
if (!drv_data || fimc->index >= drv_data->num_instances ||
fimc->index < 0) {

View file

@ -1149,7 +1149,6 @@ static void fimc_md_put_clocks(struct fimc_md *fmd)
while (--i >= 0) {
if (IS_ERR(fmd->camclk[i].clock))
continue;
clk_unprepare(fmd->camclk[i].clock);
clk_put(fmd->camclk[i].clock);
fmd->camclk[i].clock = ERR_PTR(-EINVAL);
}
@ -1168,7 +1167,7 @@ static int fimc_md_get_clocks(struct fimc_md *fmd)
struct device *dev = NULL;
char clk_name[32];
struct clk *clock;
int ret, i;
int i, ret = 0;
for (i = 0; i < FIMC_MAX_CAMCLKS; i++)
fmd->camclk[i].clock = ERR_PTR(-EINVAL);
@ -1186,12 +1185,6 @@ static int fimc_md_get_clocks(struct fimc_md *fmd)
ret = PTR_ERR(clock);
break;
}
ret = clk_prepare(clock);
if (ret < 0) {
clk_put(clock);
fmd->camclk[i].clock = ERR_PTR(-EINVAL);
break;
}
fmd->camclk[i].clock = clock;
}
if (ret)
@ -1248,7 +1241,7 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd,
ret = pm_runtime_get_sync(fmd->pmf);
if (ret < 0)
return ret;
ret = clk_enable(camclk->clock);
ret = clk_prepare_enable(camclk->clock);
dbg("Enabled camclk %d: f: %lu", si->clk_id,
clk_get_rate(camclk->clock));
}
@ -1259,7 +1252,7 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd,
return 0;
if (--camclk->use_count == 0) {
clk_disable(camclk->clock);
clk_disable_unprepare(camclk->clock);
pm_runtime_put(fmd->pmf);
dbg("Disabled camclk %d", si->clk_id);
}
@ -1529,9 +1522,9 @@ static int fimc_md_probe(struct platform_device *pdev)
err_unlock:
mutex_unlock(&fmd->media_dev.graph_mutex);
err_clk:
media_device_unregister(&fmd->media_dev);
fimc_md_put_clocks(fmd);
fimc_md_unregister_entities(fmd);
media_device_unregister(&fmd->media_dev);
err_md:
v4l2_device_unregister(&fmd->v4l2_dev);
return ret;
@ -1543,6 +1536,8 @@ static int fimc_md_remove(struct platform_device *pdev)
if (!fmd)
return 0;
v4l2_device_unregister(&fmd->v4l2_dev);
device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
fimc_md_unregister_entities(fmd);
fimc_md_pipelines_free(fmd);

View file

@ -399,7 +399,7 @@ static void cafe_ctlr_init(struct mcam_camera *mcam)
}
static void cafe_ctlr_power_up(struct mcam_camera *mcam)
static int cafe_ctlr_power_up(struct mcam_camera *mcam)
{
/*
* Part one of the sensor dance: turn the global
@ -414,6 +414,8 @@ static void cafe_ctlr_power_up(struct mcam_camera *mcam)
*/
mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
return 0;
}
static void cafe_ctlr_power_down(struct mcam_camera *mcam)

View file

@ -19,6 +19,7 @@
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@ -93,6 +94,9 @@ MODULE_PARM_DESC(buffer_mode,
#define CF_CONFIG_NEEDED 4 /* Must configure hardware */
#define CF_SINGLE_BUFFER 5 /* Running with a single buffer */
#define CF_SG_RESTART 6 /* SG restart needed */
#define CF_FRAME_SOF0 7 /* Frame 0 started */
#define CF_FRAME_SOF1 8
#define CF_FRAME_SOF2 9
#define sensor_call(cam, o, f, args...) \
v4l2_subdev_call(cam->sensor, o, f, ##args)
@ -101,6 +105,7 @@ static struct mcam_format_struct {
__u8 *desc;
__u32 pixelformat;
int bpp; /* Bytes per pixel */
bool planar;
enum v4l2_mbus_pixelcode mbus_code;
} mcam_formats[] = {
{
@ -108,24 +113,56 @@ static struct mcam_format_struct {
.pixelformat = V4L2_PIX_FMT_YUYV,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = false,
},
{
.desc = "UYVY 4:2:2",
.pixelformat = V4L2_PIX_FMT_UYVY,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = false,
},
{
.desc = "YUV 4:2:2 PLANAR",
.pixelformat = V4L2_PIX_FMT_YUV422P,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = true,
},
{
.desc = "YUV 4:2:0 PLANAR",
.pixelformat = V4L2_PIX_FMT_YUV420,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = true,
},
{
.desc = "YVU 4:2:0 PLANAR",
.pixelformat = V4L2_PIX_FMT_YVU420,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = true,
},
{
.desc = "RGB 444",
.pixelformat = V4L2_PIX_FMT_RGB444,
.mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
.bpp = 2,
.planar = false,
},
{
.desc = "RGB 565",
.pixelformat = V4L2_PIX_FMT_RGB565,
.mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
.bpp = 2,
.planar = false,
},
{
.desc = "Raw RGB Bayer",
.pixelformat = V4L2_PIX_FMT_SBGGR8,
.mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
.bpp = 1
.bpp = 1,
.planar = false,
},
};
#define N_MCAM_FMTS ARRAY_SIZE(mcam_formats)
@ -168,6 +205,12 @@ struct mcam_dma_desc {
u32 segment_len;
};
struct yuv_pointer_t {
dma_addr_t y;
dma_addr_t u;
dma_addr_t v;
};
/*
* Our buffer type for working with videobuf2. Note that the vb2
* developers have decreed that struct vb2_buffer must be at the
@ -179,6 +222,7 @@ struct mcam_vb_buffer {
struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */
dma_addr_t dma_desc_pa; /* Descriptor physical address */
int dma_desc_nent; /* Number of mapped descriptors */
struct yuv_pointer_t yuv_p;
};
static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb)
@ -219,8 +263,10 @@ static void mcam_reset_buffers(struct mcam_camera *cam)
int i;
cam->next_buf = -1;
for (i = 0; i < cam->nbufs; i++)
for (i = 0; i < cam->nbufs; i++) {
clear_bit(i, &cam->flags);
clear_bit(CF_FRAME_SOF0 + i, &cam->flags);
}
}
static inline int mcam_needs_config(struct mcam_camera *cam)
@ -253,6 +299,45 @@ static void mcam_ctlr_stop(struct mcam_camera *cam)
mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
}
static void mcam_enable_mipi(struct mcam_camera *mcam)
{
/* Using MIPI mode and enable MIPI */
cam_dbg(mcam, "camera: DPHY3=0x%x, DPHY5=0x%x, DPHY6=0x%x\n",
mcam->dphy[0], mcam->dphy[1], mcam->dphy[2]);
mcam_reg_write(mcam, REG_CSI2_DPHY3, mcam->dphy[0]);
mcam_reg_write(mcam, REG_CSI2_DPHY5, mcam->dphy[1]);
mcam_reg_write(mcam, REG_CSI2_DPHY6, mcam->dphy[2]);
if (!mcam->mipi_enabled) {
if (mcam->lane > 4 || mcam->lane <= 0) {
cam_warn(mcam, "lane number error\n");
mcam->lane = 1; /* set the default value */
}
/*
* 0x41 actives 1 lane
* 0x43 actives 2 lanes
* 0x45 actives 3 lanes (never happen)
* 0x47 actives 4 lanes
*/
mcam_reg_write(mcam, REG_CSI2_CTRL0,
CSI2_C0_MIPI_EN | CSI2_C0_ACT_LANE(mcam->lane));
mcam_reg_write(mcam, REG_CLKCTRL,
(mcam->mclk_src << 29) | mcam->mclk_div);
mcam->mipi_enabled = true;
}
}
static void mcam_disable_mipi(struct mcam_camera *mcam)
{
/* Using Parallel mode or disable MIPI */
mcam_reg_write(mcam, REG_CSI2_CTRL0, 0x0);
mcam_reg_write(mcam, REG_CSI2_DPHY3, 0x0);
mcam_reg_write(mcam, REG_CSI2_DPHY5, 0x0);
mcam_reg_write(mcam, REG_CSI2_DPHY6, 0x0);
mcam->mipi_enabled = false;
}
/* ------------------------------------------------------------------- */
#ifdef MCAM_MODE_VMALLOC
@ -425,6 +510,15 @@ static inline int mcam_check_dma_buffers(struct mcam_camera *cam)
/*
* DMA-contiguous code.
*/
static bool mcam_fmt_is_planar(__u32 pfmt)
{
struct mcam_format_struct *f;
f = mcam_find_format(pfmt);
return f->planar;
}
/*
* Set up a contiguous buffer for the given frame. Here also is where
* the underrun strategy is set: if there is no buffer available, reuse
@ -436,27 +530,58 @@ static inline int mcam_check_dma_buffers(struct mcam_camera *cam)
static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
{
struct mcam_vb_buffer *buf;
struct v4l2_pix_format *fmt = &cam->pix_format;
dma_addr_t dma_handle;
u32 pixel_count = fmt->width * fmt->height;
struct vb2_buffer *vb;
/*
* If there are no available buffers, go into single mode
*/
if (list_empty(&cam->buffers)) {
buf = cam->vb_bufs[frame ^ 0x1];
cam->vb_bufs[frame] = buf;
mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
set_bit(CF_SINGLE_BUFFER, &cam->flags);
cam->frame_state.singles++;
return;
} else {
/*
* OK, we have a buffer we can use.
*/
buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer,
queue);
list_del_init(&buf->queue);
clear_bit(CF_SINGLE_BUFFER, &cam->flags);
}
/*
* OK, we have a buffer we can use.
*/
buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
list_del_init(&buf->queue);
mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
cam->vb_bufs[frame] = buf;
clear_bit(CF_SINGLE_BUFFER, &cam->flags);
vb = &buf->vb_buf;
dma_handle = vb2_dma_contig_plane_dma_addr(vb, 0);
buf->yuv_p.y = dma_handle;
switch (cam->pix_format.pixelformat) {
case V4L2_PIX_FMT_YUV422P:
buf->yuv_p.u = buf->yuv_p.y + pixel_count;
buf->yuv_p.v = buf->yuv_p.u + pixel_count / 2;
break;
case V4L2_PIX_FMT_YUV420:
buf->yuv_p.u = buf->yuv_p.y + pixel_count;
buf->yuv_p.v = buf->yuv_p.u + pixel_count / 4;
break;
case V4L2_PIX_FMT_YVU420:
buf->yuv_p.v = buf->yuv_p.y + pixel_count;
buf->yuv_p.u = buf->yuv_p.v + pixel_count / 4;
break;
default:
break;
}
mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, buf->yuv_p.y);
if (mcam_fmt_is_planar(fmt->pixelformat)) {
mcam_reg_write(cam, frame == 0 ?
REG_U0BAR : REG_U1BAR, buf->yuv_p.u);
mcam_reg_write(cam, frame == 0 ?
REG_V0BAR : REG_V1BAR, buf->yuv_p.v);
}
}
/*
@ -614,48 +739,90 @@ static inline void mcam_sg_restart(struct mcam_camera *cam)
*/
static void mcam_ctlr_image(struct mcam_camera *cam)
{
int imgsz;
struct v4l2_pix_format *fmt = &cam->pix_format;
u32 widthy = 0, widthuv = 0, imgsz_h, imgsz_w;
cam_dbg(cam, "camera: bytesperline = %d; height = %d\n",
fmt->bytesperline, fmt->sizeimage / fmt->bytesperline);
imgsz_h = (fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK;
imgsz_w = (fmt->width * 2) & IMGSZ_H_MASK;
switch (fmt->pixelformat) {
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
widthy = fmt->width * 2;
widthuv = 0;
break;
case V4L2_PIX_FMT_JPEG:
imgsz_h = (fmt->sizeimage / fmt->bytesperline) << IMGSZ_V_SHIFT;
widthy = fmt->bytesperline;
widthuv = 0;
break;
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
widthy = fmt->width;
widthuv = fmt->width / 2;
break;
default:
widthy = fmt->bytesperline;
widthuv = 0;
}
mcam_reg_write_mask(cam, REG_IMGPITCH, widthuv << 16 | widthy,
IMGP_YP_MASK | IMGP_UVP_MASK);
mcam_reg_write(cam, REG_IMGSIZE, imgsz_h | imgsz_w);
mcam_reg_write(cam, REG_IMGOFFSET, 0x0);
imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
(fmt->bytesperline & IMGSZ_H_MASK);
mcam_reg_write(cam, REG_IMGSIZE, imgsz);
mcam_reg_write(cam, REG_IMGOFFSET, 0);
/* YPITCH just drops the last two bits */
mcam_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
IMGP_YP_MASK);
/*
* Tell the controller about the image format we are using.
*/
switch (cam->pix_format.pixelformat) {
switch (fmt->pixelformat) {
case V4L2_PIX_FMT_YUV422P:
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_YUV | C0_YUV_PLANAR | C0_YUVE_YVYU, C0_DF_MASK);
break;
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_YUV | C0_YUV_420PL | C0_YUVE_YVYU, C0_DF_MASK);
break;
case V4L2_PIX_FMT_YUYV:
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
C0_DF_MASK);
break;
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_UYVY, C0_DF_MASK);
break;
case V4L2_PIX_FMT_UYVY:
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK);
break;
case V4L2_PIX_FMT_JPEG:
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK);
break;
case V4L2_PIX_FMT_RGB444:
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
C0_DF_MASK);
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_RGB | C0_RGBF_444 | C0_RGB4_XRGB, C0_DF_MASK);
/* Alpha value? */
break;
break;
case V4L2_PIX_FMT_RGB565:
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
C0_DF_MASK);
break;
mcam_reg_write_mask(cam, REG_CTRL0,
C0_DF_RGB | C0_RGBF_565 | C0_RGB5_BGGR, C0_DF_MASK);
break;
default:
cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
break;
cam_err(cam, "camera: unknown format: %#x\n", fmt->pixelformat);
break;
}
/*
* Make sure it knows we want to use hsync/vsync.
*/
mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
C0_SIFM_MASK);
mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, C0_SIFM_MASK);
/*
* This field controls the generation of EOF(DVP only)
*/
if (cam->bus_type != V4L2_MBUS_CSI2)
mcam_reg_set_bit(cam, REG_CTRL0,
C0_EOF_VSYNC | C0_VEDGE_CTRL);
}
@ -753,15 +920,21 @@ static void mcam_ctlr_stop_dma(struct mcam_camera *cam)
/*
* Power up and down.
*/
static void mcam_ctlr_power_up(struct mcam_camera *cam)
static int mcam_ctlr_power_up(struct mcam_camera *cam)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&cam->dev_lock, flags);
cam->plat_power_up(cam);
ret = cam->plat_power_up(cam);
if (ret) {
spin_unlock_irqrestore(&cam->dev_lock, flags);
return ret;
}
mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
spin_unlock_irqrestore(&cam->dev_lock, flags);
msleep(5); /* Just to be sure */
return 0;
}
static void mcam_ctlr_power_down(struct mcam_camera *cam)
@ -869,6 +1042,17 @@ static int mcam_read_setup(struct mcam_camera *cam)
spin_lock_irqsave(&cam->dev_lock, flags);
clear_bit(CF_DMA_ACTIVE, &cam->flags);
mcam_reset_buffers(cam);
/*
* Update CSI2_DPHY value
*/
if (cam->calc_dphy)
cam->calc_dphy(cam);
cam_dbg(cam, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n",
cam->dphy[0], cam->dphy[1], cam->dphy[2]);
if (cam->bus_type == V4L2_MBUS_CSI2)
mcam_enable_mipi(cam);
else
mcam_disable_mipi(cam);
mcam_ctlr_irq_enable(cam);
cam->state = S_STREAMING;
if (!test_bit(CF_SG_RESTART, &cam->flags))
@ -943,6 +1127,7 @@ static void mcam_vb_wait_finish(struct vb2_queue *vq)
static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct mcam_camera *cam = vb2_get_drv_priv(vq);
unsigned int frame;
if (cam->state != S_IDLE) {
INIT_LIST_HEAD(&cam->buffers);
@ -960,6 +1145,14 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
cam->state = S_BUFWAIT;
return 0;
}
/*
* Ensure clear the left over frame flags
* before every really start streaming
*/
for (frame = 0; frame < cam->nbufs; frame++)
clear_bit(CF_FRAME_SOF0 + frame, &cam->flags);
return mcam_read_setup(cam);
}
@ -976,6 +1169,12 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq)
if (cam->state != S_STREAMING)
return -EINVAL;
mcam_ctlr_stop_dma(cam);
/*
* Reset the CCIC PHY after stopping streaming,
* otherwise, the CCIC may be unstable.
*/
if (cam->ctlr_reset)
cam->ctlr_reset(cam);
/*
* VB2 reclaims the buffers, so we need to forget
* about them.
@ -1087,6 +1286,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
#ifdef MCAM_MODE_DMA_CONTIG
vq->ops = &mcam_vb2_ops;
vq->mem_ops = &vb2_dma_contig_memops;
vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
vq->io_modes = VB2_MMAP | VB2_USERPTR;
cam->dma_setup = mcam_ctlr_dma_contig;
@ -1097,6 +1297,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
#ifdef MCAM_MODE_DMA_SG
vq->ops = &mcam_vb2_sg_ops;
vq->mem_ops = &vb2_dma_sg_memops;
vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
vq->io_modes = VB2_MMAP | VB2_USERPTR;
cam->dma_setup = mcam_ctlr_dma_sg;
cam->frame_complete = mcam_dma_sg_done;
@ -1247,7 +1448,15 @@ static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
mutex_unlock(&cam->s_mutex);
v4l2_fill_pix_format(pix, &mbus_fmt);
pix->bytesperline = pix->width * f->bpp;
switch (f->pixelformat) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
pix->bytesperline = pix->width * 3 / 2;
break;
default:
pix->bytesperline = pix->width * f->bpp;
break;
}
pix->sizeimage = pix->height * pix->bytesperline;
return ret;
}
@ -1475,7 +1684,9 @@ static int mcam_v4l_open(struct file *filp)
ret = mcam_setup_vb2(cam);
if (ret)
goto out;
mcam_ctlr_power_up(cam);
ret = mcam_ctlr_power_up(cam);
if (ret)
goto out;
__mcam_cam_reset(cam);
mcam_set_config_needed(cam, 1);
}
@ -1498,10 +1709,12 @@ static int mcam_v4l_release(struct file *filp)
if (cam->users == 0) {
mcam_ctlr_stop_dma(cam);
mcam_cleanup_vb2(cam);
mcam_disable_mipi(cam);
mcam_ctlr_power_down(cam);
if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
mcam_free_dma_bufs(cam);
}
mutex_unlock(&cam->s_mutex);
return 0;
}
@ -1617,9 +1830,11 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
* each time.
*/
for (frame = 0; frame < cam->nbufs; frame++)
if (irqs & (IRQ_EOF0 << frame)) {
if (irqs & (IRQ_EOF0 << frame) &&
test_bit(CF_FRAME_SOF0 + frame, &cam->flags)) {
mcam_frame_complete(cam, frame);
handled = 1;
clear_bit(CF_FRAME_SOF0 + frame, &cam->flags);
if (cam->buffer_mode == B_DMA_sg)
break;
}
@ -1628,9 +1843,15 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
* code assumes that we won't get multiple frame interrupts
* at once; may want to rethink that.
*/
if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) {
for (frame = 0; frame < cam->nbufs; frame++) {
if (irqs & (IRQ_SOF0 << frame)) {
set_bit(CF_FRAME_SOF0 + frame, &cam->flags);
handled = IRQ_HANDLED;
}
}
if (handled == IRQ_HANDLED) {
set_bit(CF_DMA_ACTIVE, &cam->flags);
handled = 1;
if (cam->buffer_mode == B_DMA_sg)
mcam_ctlr_stop(cam);
}
@ -1787,7 +2008,11 @@ int mccic_resume(struct mcam_camera *cam)
mutex_lock(&cam->s_mutex);
if (cam->users > 0) {
mcam_ctlr_power_up(cam);
ret = mcam_ctlr_power_up(cam);
if (ret) {
mutex_unlock(&cam->s_mutex);
return ret;
}
__mcam_cam_reset(cam);
} else {
mcam_ctlr_power_down(cam);

View file

@ -88,6 +88,8 @@ struct mcam_frame_state {
unsigned int delivered;
};
#define NR_MCAM_CLK 3
/*
* A description of one of our devices.
* Locking: controlled by s_mutex. Certain fields, however, require
@ -108,11 +110,33 @@ struct mcam_camera {
short int clock_speed; /* Sensor clock speed, default 30 */
short int use_smbus; /* SMBUS or straight I2c? */
enum mcam_buffer_mode buffer_mode;
int mclk_min; /* The minimal value of mclk */
int mclk_src; /* which clock source the mclk derives from */
int mclk_div; /* Clock Divider Value for MCLK */
int ccic_id;
enum v4l2_mbus_type bus_type;
/* MIPI support */
/* The dphy config value, allocated in board file
* dphy[0]: DPHY3
* dphy[1]: DPHY5
* dphy[2]: DPHY6
*/
int *dphy;
bool mipi_enabled; /* flag whether mipi is enabled already */
int lane; /* lane number */
/* clock tree support */
struct clk *clk[NR_MCAM_CLK];
/*
* Callbacks from the core to the platform code.
*/
void (*plat_power_up) (struct mcam_camera *cam);
int (*plat_power_up) (struct mcam_camera *cam);
void (*plat_power_down) (struct mcam_camera *cam);
void (*calc_dphy) (struct mcam_camera *cam);
void (*ctlr_reset) (struct mcam_camera *cam);
/*
* Everything below here is private to the mcam core and
@ -225,6 +249,23 @@ int mccic_resume(struct mcam_camera *cam);
#define REG_Y0BAR 0x00
#define REG_Y1BAR 0x04
#define REG_Y2BAR 0x08
#define REG_U0BAR 0x0c
#define REG_U1BAR 0x10
#define REG_U2BAR 0x14
#define REG_V0BAR 0x18
#define REG_V1BAR 0x1C
#define REG_V2BAR 0x20
/*
* register definitions for MIPI support
*/
#define REG_CSI2_CTRL0 0x100
#define CSI2_C0_MIPI_EN (0x1 << 0)
#define CSI2_C0_ACT_LANE(n) ((n-1) << 1)
#define REG_CSI2_DPHY3 0x12c
#define REG_CSI2_DPHY5 0x134
#define REG_CSI2_DPHY6 0x138
/* ... */
#define REG_IMGPITCH 0x24 /* Image pitch register */
@ -293,13 +334,16 @@ int mccic_resume(struct mcam_camera *cam);
#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */
#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */
/* Bayer bits 18,19 if needed */
#define C0_EOF_VSYNC 0x00400000 /* Generate EOF by VSYNC */
#define C0_VEDGE_CTRL 0x00800000 /* Detect falling edge of VSYNC */
#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */
#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */
#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */
#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */
#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
/* SIFMODE */
#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */
#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
#define C0_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
/* Bits below C1_444ALPHA are not present in Cafe */
#define REG_CTRL1 0x40 /* Control 1 */

View file

@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/pm.h>
#include <linux/clk.h>
#include "mcam-core.h"
@ -33,11 +34,14 @@ MODULE_ALIAS("platform:mmp-camera");
MODULE_AUTHOR("Jonathan Corbet <[email protected]>");
MODULE_LICENSE("GPL");
static char *mcam_clks[] = {"CCICAXICLK", "CCICFUNCLK", "CCICPHYCLK"};
struct mmp_camera {
void *power_regs;
struct platform_device *pdev;
struct mcam_camera mcam;
struct list_head devlist;
struct clk *mipi_clk;
int irq;
};
@ -101,6 +105,27 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
#define CPU_SUBSYS_PMU_BASE 0xd4282800
#define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */
#define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */
#define REG_CCIC2_CRCR 0xf4 /* CCIC2 clk reset ctrl reg */
static void mcam_clk_enable(struct mcam_camera *mcam)
{
unsigned int i;
for (i = 0; i < NR_MCAM_CLK; i++) {
if (!IS_ERR(mcam->clk[i]))
clk_prepare_enable(mcam->clk[i]);
}
}
static void mcam_clk_disable(struct mcam_camera *mcam)
{
int i;
for (i = NR_MCAM_CLK - 1; i >= 0; i--) {
if (!IS_ERR(mcam->clk[i]))
clk_disable_unprepare(mcam->clk[i]);
}
}
/*
* Power control.
@ -112,10 +137,17 @@ static void mmpcam_power_up_ctlr(struct mmp_camera *cam)
mdelay(1);
}
static void mmpcam_power_up(struct mcam_camera *mcam)
static int mmpcam_power_up(struct mcam_camera *mcam)
{
struct mmp_camera *cam = mcam_to_cam(mcam);
struct mmp_camera_platform_data *pdata;
if (mcam->bus_type == V4L2_MBUS_CSI2) {
cam->mipi_clk = devm_clk_get(mcam->dev, "mipi");
if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0))
return PTR_ERR(cam->mipi_clk);
}
/*
* Turn on power and clocks to the controller.
*/
@ -132,6 +164,10 @@ static void mmpcam_power_up(struct mcam_camera *mcam)
mdelay(5);
gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */
mdelay(5);
mcam_clk_enable(mcam);
return 0;
}
static void mmpcam_power_down(struct mcam_camera *mcam)
@ -149,8 +185,133 @@ static void mmpcam_power_down(struct mcam_camera *mcam)
pdata = cam->pdev->dev.platform_data;
gpio_set_value(pdata->sensor_power_gpio, 0);
gpio_set_value(pdata->sensor_reset_gpio, 0);
if (mcam->bus_type == V4L2_MBUS_CSI2 && !IS_ERR(cam->mipi_clk)) {
if (cam->mipi_clk)
devm_clk_put(mcam->dev, cam->mipi_clk);
cam->mipi_clk = NULL;
}
mcam_clk_disable(mcam);
}
void mcam_ctlr_reset(struct mcam_camera *mcam)
{
unsigned long val;
struct mmp_camera *cam = mcam_to_cam(mcam);
if (mcam->ccic_id) {
/*
* Using CCIC2
*/
val = ioread32(cam->power_regs + REG_CCIC2_CRCR);
iowrite32(val & ~0x2, cam->power_regs + REG_CCIC2_CRCR);
iowrite32(val | 0x2, cam->power_regs + REG_CCIC2_CRCR);
} else {
/*
* Using CCIC1
*/
val = ioread32(cam->power_regs + REG_CCIC_CRCR);
iowrite32(val & ~0x2, cam->power_regs + REG_CCIC_CRCR);
iowrite32(val | 0x2, cam->power_regs + REG_CCIC_CRCR);
}
}
/*
* calc the dphy register values
* There are three dphy registers being used.
* dphy[0] - CSI2_DPHY3
* dphy[1] - CSI2_DPHY5
* dphy[2] - CSI2_DPHY6
* CSI2_DPHY3 and CSI2_DPHY6 can be set with a default value
* or be calculated dynamically
*/
void mmpcam_calc_dphy(struct mcam_camera *mcam)
{
struct mmp_camera *cam = mcam_to_cam(mcam);
struct mmp_camera_platform_data *pdata = cam->pdev->dev.platform_data;
struct device *dev = &cam->pdev->dev;
unsigned long tx_clk_esc;
/*
* If CSI2_DPHY3 is calculated dynamically,
* pdata->lane_clk should be already set
* either in the board driver statically
* or in the sensor driver dynamically.
*/
/*
* dphy[0] - CSI2_DPHY3:
* bit 0 ~ bit 7: HS Term Enable.
* defines the time that the DPHY
* wait before enabling the data
* lane termination after detecting
* that the sensor has driven the data
* lanes to the LP00 bridge state.
* The value is calculated by:
* (Max T(D_TERM_EN)/Period(DDR)) - 1
* bit 8 ~ bit 15: HS_SETTLE
* Time interval during which the HS
* receiver shall ignore any Data Lane
* HS transistions.
* The vaule has been calibrated on
* different boards. It seems to work well.
*
* More detail please refer
* MIPI Alliance Spectification for D-PHY
* document for explanation of HS-SETTLE
* and D-TERM-EN.
*/
switch (pdata->dphy3_algo) {
case DPHY3_ALGO_PXA910:
/*
* Calculate CSI2_DPHY3 algo for PXA910
*/
pdata->dphy[0] =
(((1 + (pdata->lane_clk * 80) / 1000) & 0xff) << 8)
| (1 + pdata->lane_clk * 35 / 1000);
break;
case DPHY3_ALGO_PXA2128:
/*
* Calculate CSI2_DPHY3 algo for PXA2128
*/
pdata->dphy[0] =
(((2 + (pdata->lane_clk * 110) / 1000) & 0xff) << 8)
| (1 + pdata->lane_clk * 35 / 1000);
break;
default:
/*
* Use default CSI2_DPHY3 value for PXA688/PXA988
*/
dev_dbg(dev, "camera: use the default CSI2_DPHY3 value\n");
}
/*
* mipi_clk will never be changed, it is a fixed value on MMP
*/
if (IS_ERR(cam->mipi_clk))
return;
/* get the escape clk, this is hard coded */
tx_clk_esc = (clk_get_rate(cam->mipi_clk) / 1000000) / 12;
/*
* dphy[2] - CSI2_DPHY6:
* bit 0 ~ bit 7: CK Term Enable
* Time for the Clock Lane receiver to enable the HS line
* termination. The value is calculated similarly with
* HS Term Enable
* bit 8 ~ bit 15: CK Settle
* Time interval during which the HS receiver shall ignore
* any Clock Lane HS transitions.
* The value is calibrated on the boards.
*/
pdata->dphy[2] =
((((534 * tx_clk_esc) / 2000 - 1) & 0xff) << 8)
| (((38 * tx_clk_esc) / 1000 - 1) & 0xff);
dev_dbg(dev, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n",
pdata->dphy[0], pdata->dphy[1], pdata->dphy[2]);
}
static irqreturn_t mmpcam_irq(int irq, void *data)
{
@ -164,6 +325,35 @@ static irqreturn_t mmpcam_irq(int irq, void *data)
return IRQ_RETVAL(handled);
}
static void mcam_deinit_clk(struct mcam_camera *mcam)
{
unsigned int i;
for (i = 0; i < NR_MCAM_CLK; i++) {
if (!IS_ERR(mcam->clk[i])) {
if (mcam->clk[i])
devm_clk_put(mcam->dev, mcam->clk[i]);
}
mcam->clk[i] = NULL;
}
}
static void mcam_init_clk(struct mcam_camera *mcam)
{
unsigned int i;
for (i = 0; i < NR_MCAM_CLK; i++) {
if (mcam_clks[i] != NULL) {
/* Some clks are not necessary on some boards
* We still try to run even it fails getting clk
*/
mcam->clk[i] = devm_clk_get(mcam->dev, mcam_clks[i]);
if (IS_ERR(mcam->clk[i]))
dev_warn(mcam->dev, "Could not get clk: %s\n",
mcam_clks[i]);
}
}
}
static int mmpcam_probe(struct platform_device *pdev)
{
@ -173,17 +363,32 @@ static int mmpcam_probe(struct platform_device *pdev)
struct mmp_camera_platform_data *pdata;
int ret;
cam = kzalloc(sizeof(*cam), GFP_KERNEL);
pdata = pdev->dev.platform_data;
if (!pdata)
return -ENODEV;
cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL);
if (cam == NULL)
return -ENOMEM;
cam->pdev = pdev;
cam->mipi_clk = NULL;
INIT_LIST_HEAD(&cam->devlist);
mcam = &cam->mcam;
mcam->plat_power_up = mmpcam_power_up;
mcam->plat_power_down = mmpcam_power_down;
mcam->ctlr_reset = mcam_ctlr_reset;
mcam->calc_dphy = mmpcam_calc_dphy;
mcam->dev = &pdev->dev;
mcam->use_smbus = 0;
mcam->ccic_id = pdev->id;
mcam->mclk_min = pdata->mclk_min;
mcam->mclk_src = pdata->mclk_src;
mcam->mclk_div = pdata->mclk_div;
mcam->bus_type = pdata->bus_type;
mcam->dphy = pdata->dphy;
mcam->mipi_enabled = false;
mcam->lane = pdata->lane;
mcam->chip_id = MCAM_ARMADA610;
mcam->buffer_mode = B_DMA_sg;
spin_lock_init(&mcam->dev_lock);
@ -191,69 +396,58 @@ static int mmpcam_probe(struct platform_device *pdev)
* Get our I/O memory.
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "no iomem resource!\n");
ret = -ENODEV;
goto out_free;
}
mcam->regs = ioremap(res->start, resource_size(res));
if (mcam->regs == NULL) {
dev_err(&pdev->dev, "MMIO ioremap fail\n");
ret = -ENODEV;
goto out_free;
}
mcam->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mcam->regs))
return PTR_ERR(mcam->regs);
mcam->regs_size = resource_size(res);
/*
* Power/clock memory is elsewhere; get it too. Perhaps this
* should really be managed outside of this driver?
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res == NULL) {
dev_err(&pdev->dev, "no power resource!\n");
ret = -ENODEV;
goto out_unmap1;
}
cam->power_regs = ioremap(res->start, resource_size(res));
if (cam->power_regs == NULL) {
dev_err(&pdev->dev, "power MMIO ioremap fail\n");
ret = -ENODEV;
goto out_unmap1;
}
cam->power_regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(cam->power_regs))
return PTR_ERR(cam->power_regs);
/*
* Find the i2c adapter. This assumes, of course, that the
* i2c bus is already up and functioning.
*/
pdata = pdev->dev.platform_data;
mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
if (mcam->i2c_adapter == NULL) {
ret = -ENODEV;
dev_err(&pdev->dev, "No i2c adapter\n");
goto out_unmap2;
return -ENODEV;
}
/*
* Sensor GPIO pins.
*/
ret = gpio_request(pdata->sensor_power_gpio, "cam-power");
ret = devm_gpio_request(&pdev->dev, pdata->sensor_power_gpio,
"cam-power");
if (ret) {
dev_err(&pdev->dev, "Can't get sensor power gpio %d",
pdata->sensor_power_gpio);
goto out_unmap2;
return ret;
}
gpio_direction_output(pdata->sensor_power_gpio, 0);
ret = gpio_request(pdata->sensor_reset_gpio, "cam-reset");
ret = devm_gpio_request(&pdev->dev, pdata->sensor_reset_gpio,
"cam-reset");
if (ret) {
dev_err(&pdev->dev, "Can't get sensor reset gpio %d",
pdata->sensor_reset_gpio);
goto out_gpio;
return ret;
}
gpio_direction_output(pdata->sensor_reset_gpio, 0);
mcam_init_clk(mcam);
/*
* Power the device up and hand it off to the core.
*/
mmpcam_power_up(mcam);
ret = mmpcam_power_up(mcam);
if (ret)
goto out_deinit_clk;
ret = mccic_register(mcam);
if (ret)
goto out_gpio2;
goto out_power_down;
/*
* Finally, set up our IRQ now that the core is ready to
* deal with it.
@ -264,8 +458,8 @@ static int mmpcam_probe(struct platform_device *pdev)
goto out_unregister;
}
cam->irq = res->start;
ret = request_irq(cam->irq, mmpcam_irq, IRQF_SHARED,
"mmp-camera", mcam);
ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED,
"mmp-camera", mcam);
if (ret == 0) {
mmpcam_add_device(cam);
return 0;
@ -273,17 +467,10 @@ static int mmpcam_probe(struct platform_device *pdev)
out_unregister:
mccic_shutdown(mcam);
out_gpio2:
out_power_down:
mmpcam_power_down(mcam);
gpio_free(pdata->sensor_reset_gpio);
out_gpio:
gpio_free(pdata->sensor_power_gpio);
out_unmap2:
iounmap(cam->power_regs);
out_unmap1:
iounmap(mcam->regs);
out_free:
kfree(cam);
out_deinit_clk:
mcam_deinit_clk(mcam);
return ret;
}
@ -300,6 +487,7 @@ static int mmpcam_remove(struct mmp_camera *cam)
pdata = cam->pdev->dev.platform_data;
gpio_free(pdata->sensor_reset_gpio);
gpio_free(pdata->sensor_power_gpio);
mcam_deinit_clk(mcam);
iounmap(cam->power_regs);
iounmap(mcam->regs);
kfree(cam);

View file

@ -106,15 +106,15 @@ static const u32 src_pixfmt_map[8][2] = {
void camif_hw_set_source_format(struct camif_dev *camif)
{
struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt;
unsigned int i = ARRAY_SIZE(src_pixfmt_map);
int i;
u32 cfg;
while (i-- >= 0) {
for (i = ARRAY_SIZE(src_pixfmt_map) - 1; i >= 0; i--) {
if (src_pixfmt_map[i][0] == mf->code)
break;
}
if (i == 0 && src_pixfmt_map[i][0] != mf->code) {
if (i < 0) {
i = 0;
dev_err(camif->dev,
"Unsupported pixel code, falling back to %#08x\n",
src_pixfmt_map[i][0]);

View file

@ -374,9 +374,9 @@
#define S5P_FIMV_NUM_PIXELS_IN_MB_COL_V6 16
/* Buffer size requirements defined by hardware */
#define S5P_FIMV_TMV_BUFFER_SIZE_V6(w, h) (((w) + 1) * ((h) + 1) * 8)
#define S5P_FIMV_TMV_BUFFER_SIZE_V6(w, h) (((w) + 1) * ((h) + 3) * 8)
#define S5P_FIMV_ME_BUFFER_SIZE_V6(imw, imh, mbw, mbh) \
((DIV_ROUND_UP(imw, 64) * DIV_ROUND_UP(imh, 64) * 256) + \
(((((imw + 127) / 64) * 16) * DIV_ROUND_UP(imh, 64) * 256) + \
(DIV_ROUND_UP((mbw) * (mbh), 32) * 16))
#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(w, h) (((w) * 192) + 64)
#define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(w, h) \

View file

@ -0,0 +1,61 @@
/*
* Register definition file for Samsung MFC V7.x Interface (FIMV) driver
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _REGS_MFC_V7_H
#define _REGS_MFC_V7_H
#include "regs-mfc-v6.h"
/* Additional features of v7 */
#define S5P_FIMV_CODEC_VP8_ENC_V7 25
/* Additional registers for v7 */
#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7 0xf47c
#define S5P_FIMV_E_SOURCE_FIRST_ADDR_V7 0xf9e0
#define S5P_FIMV_E_SOURCE_SECOND_ADDR_V7 0xf9e4
#define S5P_FIMV_E_SOURCE_THIRD_ADDR_V7 0xf9e8
#define S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7 0xf9ec
#define S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7 0xf9f0
#define S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7 0xf9f4
#define S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7 0xfa70
#define S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7 0xfa74
#define S5P_FIMV_E_VP8_OPTIONS_V7 0xfdb0
#define S5P_FIMV_E_VP8_FILTER_OPTIONS_V7 0xfdb4
#define S5P_FIMV_E_VP8_GOLDEN_FRAME_OPTION_V7 0xfdb8
#define S5P_FIMV_E_VP8_NUM_T_LAYER_V7 0xfdc4
/* MFCv7 variant defines */
#define MAX_FW_SIZE_V7 (SZ_1M) /* 1MB */
#define MAX_CPB_SIZE_V7 (3 * SZ_1M) /* 3MB */
#define MFC_VERSION_V7 0x72
#define MFC_NUM_PORTS_V7 1
#define MFC_LUMA_PAD_BYTES_V7 256
#define MFC_CHROMA_PAD_BYTES_V7 128
/* MFCv7 Context buffer sizes */
#define MFC_CTX_BUF_SIZE_V7 (30 * SZ_1K) /* 30KB */
#define MFC_H264_DEC_CTX_BUF_SIZE_V7 (2 * SZ_1M) /* 2MB */
#define MFC_OTHER_DEC_CTX_BUF_SIZE_V7 (20 * SZ_1K) /* 20KB */
#define MFC_H264_ENC_CTX_BUF_SIZE_V7 (100 * SZ_1K) /* 100KB */
#define MFC_OTHER_ENC_CTX_BUF_SIZE_V7 (10 * SZ_1K) /* 10KB */
/* Buffer size defines */
#define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(w, h) \
(SZ_1M + ((w) * 144) + (8192 * (h)) + 49216)
#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(w, h) \
(((w) * 48) + (((w) + 1) / 2 * 128) + 144 + 8192)
#endif /*_REGS_MFC_V7_H*/

View file

@ -1391,6 +1391,32 @@ static struct s5p_mfc_variant mfc_drvdata_v6 = {
.fw_name = "s5p-mfc-v6.fw",
};
struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
.dev_ctx = MFC_CTX_BUF_SIZE_V7,
.h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V7,
.other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V7,
.h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V7,
.other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V7,
};
struct s5p_mfc_buf_size buf_size_v7 = {
.fw = MAX_FW_SIZE_V7,
.cpb = MAX_CPB_SIZE_V7,
.priv = &mfc_buf_size_v7,
};
struct s5p_mfc_buf_align mfc_buf_align_v7 = {
.base = 0,
};
static struct s5p_mfc_variant mfc_drvdata_v7 = {
.version = MFC_VERSION_V7,
.port_num = MFC_NUM_PORTS_V7,
.buf_size = &buf_size_v7,
.buf_align = &mfc_buf_align_v7,
.fw_name = "s5p-mfc-v7.fw",
};
static struct platform_device_id mfc_driver_ids[] = {
{
.name = "s5p-mfc",
@ -1401,6 +1427,9 @@ static struct platform_device_id mfc_driver_ids[] = {
}, {
.name = "s5p-mfc-v6",
.driver_data = (unsigned long)&mfc_drvdata_v6,
}, {
.name = "s5p-mfc-v7",
.driver_data = (unsigned long)&mfc_drvdata_v7,
},
{},
};
@ -1413,6 +1442,9 @@ static const struct of_device_id exynos_mfc_match[] = {
}, {
.compatible = "samsung,mfc-v6",
.data = &mfc_drvdata_v6,
}, {
.compatible = "samsung,mfc-v7",
.data = &mfc_drvdata_v7,
},
{},
};

View file

@ -20,7 +20,7 @@ static struct s5p_mfc_hw_cmds *s5p_mfc_cmds;
void s5p_mfc_init_hw_cmds(struct s5p_mfc_dev *dev)
{
if (IS_MFCV6(dev))
if (IS_MFCV6_PLUS(dev))
s5p_mfc_cmds = s5p_mfc_init_hw_cmds_v6();
else
s5p_mfc_cmds = s5p_mfc_init_hw_cmds_v5();

View file

@ -108,6 +108,9 @@ static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
case S5P_MFC_CODEC_H263_ENC:
codec_type = S5P_FIMV_CODEC_H263_ENC_V6;
break;
case S5P_MFC_CODEC_VP8_ENC:
codec_type = S5P_FIMV_CODEC_VP8_ENC_V7;
break;
default:
codec_type = S5P_FIMV_CODEC_NONE_V6;
};

View file

@ -24,6 +24,7 @@
#include <media/videobuf2-core.h>
#include "regs-mfc.h"
#include "regs-mfc-v6.h"
#include "regs-mfc-v7.h"
/* Definitions related to MFC memory */
@ -64,7 +65,7 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
#define MFC_ENC_CAP_PLANE_COUNT 1
#define MFC_ENC_OUT_PLANE_COUNT 2
#define STUFF_BYTE 4
#define MFC_MAX_CTRLS 70
#define MFC_MAX_CTRLS 77
#define S5P_MFC_CODEC_NONE -1
#define S5P_MFC_CODEC_H264_DEC 0
@ -80,6 +81,7 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
#define S5P_MFC_CODEC_H264_MVC_ENC 21
#define S5P_MFC_CODEC_MPEG4_ENC 22
#define S5P_MFC_CODEC_H263_ENC 23
#define S5P_MFC_CODEC_VP8_ENC 24
#define S5P_MFC_R2H_CMD_EMPTY 0
#define S5P_MFC_R2H_CMD_SYS_INIT_RET 1
@ -407,6 +409,21 @@ struct s5p_mfc_mpeg4_enc_params {
int level;
};
/**
* struct s5p_mfc_vp8_enc_params - encoding parameters for vp8
*/
struct s5p_mfc_vp8_enc_params {
u8 imd_4x4;
enum v4l2_vp8_num_partitions num_partitions;
enum v4l2_vp8_num_ref_frames num_ref;
u8 filter_level;
u8 filter_sharpness;
u32 golden_frame_ref_period;
enum v4l2_vp8_golden_frame_sel golden_frame_sel;
u8 hier_layer;
u8 hier_layer_qp[3];
};
/**
* struct s5p_mfc_enc_params - general encoding parameters
*/
@ -441,6 +458,7 @@ struct s5p_mfc_enc_params {
struct {
struct s5p_mfc_h264_enc_params h264;
struct s5p_mfc_mpeg4_enc_params mpeg4;
struct s5p_mfc_vp8_enc_params vp8;
} codec;
};
@ -683,6 +701,7 @@ void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx);
#define HAS_PORTNUM(dev) (dev ? (dev->variant ? \
(dev->variant->port_num ? 1 : 0) : 0) : 0)
#define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0)
#define IS_MFCV6(dev) (dev->variant->version >= 0x60 ? 1 : 0)
#define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0)
#define IS_MFCV7(dev) (dev->variant->version >= 0x70 ? 1 : 0)
#endif /* S5P_MFC_COMMON_H_ */

View file

@ -164,7 +164,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev)
mfc_debug_enter();
if (IS_MFCV6(dev)) {
if (IS_MFCV6_PLUS(dev)) {
/* Reset IP */
/* except RISC, reset */
mfc_write(dev, 0xFEE, S5P_FIMV_MFC_RESET_V6);
@ -213,7 +213,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev)
static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
{
if (IS_MFCV6(dev)) {
if (IS_MFCV6_PLUS(dev)) {
mfc_write(dev, dev->bank1, S5P_FIMV_RISC_BASE_ADDRESS_V6);
mfc_debug(2, "Base Address : %08x\n", dev->bank1);
} else {
@ -226,7 +226,7 @@ static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
{
if (IS_MFCV6(dev)) {
if (IS_MFCV6_PLUS(dev)) {
/* Zero initialization should be done before RESET.
* Nothing to do here. */
} else {
@ -264,7 +264,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
s5p_mfc_clear_cmds(dev);
/* 3. Release reset signal to the RISC */
s5p_mfc_clean_dev_int_flags(dev);
if (IS_MFCV6(dev))
if (IS_MFCV6_PLUS(dev))
mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
else
mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
@ -301,7 +301,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
s5p_mfc_clock_off();
return -EIO;
}
if (IS_MFCV6(dev))
if (IS_MFCV6_PLUS(dev))
ver = mfc_read(dev, S5P_FIMV_FW_VERSION_V6);
else
ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
@ -380,7 +380,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
return ret;
}
/* 4. Release reset signal to the RISC */
if (IS_MFCV6(dev))
if (IS_MFCV6_PLUS(dev))
mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
else
mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);

View file

@ -386,7 +386,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
mfc_err("Unknown codec\n");
return -EINVAL;
}
if (!IS_MFCV6(dev)) {
if (!IS_MFCV6_PLUS(dev)) {
if (fmt->fourcc == V4L2_PIX_FMT_VP8) {
mfc_err("Not supported format.\n");
return -EINVAL;
@ -398,10 +398,11 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
mfc_err("Unsupported format for destination.\n");
return -EINVAL;
}
if (IS_MFCV6(dev) && (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) {
if (IS_MFCV6_PLUS(dev) &&
(fmt->fourcc == V4L2_PIX_FMT_NV12MT)) {
mfc_err("Not supported format.\n");
return -EINVAL;
} else if (!IS_MFCV6(dev) &&
} else if (!IS_MFCV6_PLUS(dev) &&
(fmt->fourcc != V4L2_PIX_FMT_NV12MT)) {
mfc_err("Not supported format.\n");
return -EINVAL;
@ -925,7 +926,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
psize[0] = ctx->luma_size;
psize[1] = ctx->chroma_size;
if (IS_MFCV6(dev))
if (IS_MFCV6_PLUS(dev))
allocators[0] =
ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
else
@ -1050,7 +1051,7 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q)
ctx->dpb_flush_flag = 1;
ctx->dec_dst_flag = 0;
spin_unlock_irqrestore(&dev->irqlock, flags);
if (IS_MFCV6(dev) && (ctx->state == MFCINST_RUNNING)) {
if (IS_MFCV6_PLUS(dev) && (ctx->state == MFCINST_RUNNING)) {
ctx->state = MFCINST_FLUSH;
set_work_bit_irqsave(ctx);
s5p_mfc_clean_ctx_int_flags(ctx);

View file

@ -84,6 +84,13 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_ENC,
.num_planes = 1,
},
{
.name = "VP8 Encoded Stream",
.fourcc = V4L2_PIX_FMT_VP8,
.codec_mode = S5P_MFC_CODEC_VP8_ENC,
.type = MFC_FMT_ENC,
.num_planes = 1,
},
};
#define NUM_FORMATS ARRAY_SIZE(formats)
@ -557,6 +564,60 @@ static struct mfc_control controls[] = {
.step = 1,
.default_value = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS,
.type = V4L2_CTRL_TYPE_INTEGER_MENU,
.maximum = V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS,
.default_value = V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION,
.menu_skip_mask = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES,
.type = V4L2_CTRL_TYPE_INTEGER_MENU,
.maximum = V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME,
.default_value = V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME,
.menu_skip_mask = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL,
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 0,
.maximum = 63,
.step = 1,
.default_value = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 0,
.maximum = 7,
.step = 1,
.default_value = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD,
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 0,
.maximum = (1 << 16) - 1,
.step = 1,
.default_value = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL,
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV,
.maximum = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD,
.default_value = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV,
.menu_skip_mask = 0,
},
};
#define NUM_CTRLS ARRAY_SIZE(controls)
@ -663,7 +724,7 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
spin_unlock_irqrestore(&dev->irqlock, flags);
}
if (!IS_MFCV6(dev)) {
if (!IS_MFCV6_PLUS(dev)) {
ctx->state = MFCINST_RUNNING;
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
@ -917,6 +978,11 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
return -EINVAL;
}
if (!IS_MFCV7(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) {
mfc_err("VP8 is supported only in MFC v7\n");
return -EINVAL;
}
if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
mfc_err("must be set encoding output size\n");
return -EINVAL;
@ -931,12 +997,12 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
return -EINVAL;
}
if (!IS_MFCV6(dev)) {
if (!IS_MFCV6_PLUS(dev)) {
if (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {
mfc_err("Not supported format.\n");
return -EINVAL;
}
} else if (IS_MFCV6(dev)) {
} else if (IS_MFCV6_PLUS(dev)) {
if (fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
mfc_err("Not supported format.\n");
return -EINVAL;
@ -1060,7 +1126,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
return -EINVAL;
}
if (IS_MFCV6(dev)) {
if (IS_MFCV6_PLUS(dev)) {
/* Check for min encoder buffers */
if (ctx->pb_count &&
(reqbufs->count < ctx->pb_count)) {
@ -1341,7 +1407,7 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
S5P_FIMV_ENC_PROFILE_H264_BASELINE;
break;
case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
if (IS_MFCV6(dev))
if (IS_MFCV6_PLUS(dev))
p->codec.h264.profile =
S5P_FIMV_ENC_PROFILE_H264_CONSTRAINED_BASELINE;
else
@ -1470,6 +1536,27 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
p->codec.mpeg4.quarter_pixel = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS:
p->codec.vp8.num_partitions = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4:
p->codec.vp8.imd_4x4 = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES:
p->codec.vp8.num_ref = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL:
p->codec.vp8.filter_level = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS:
p->codec.vp8.filter_sharpness = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD:
p->codec.vp8.golden_frame_ref_period = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL:
p->codec.vp8.golden_frame_sel = ctrl->val;
break;
default:
v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
ctrl->id, ctrl->val);
@ -1650,9 +1737,11 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
*buf_count = 1;
if (*buf_count > MFC_MAX_BUFFERS)
*buf_count = MFC_MAX_BUFFERS;
psize[0] = ctx->luma_size;
psize[1] = ctx->chroma_size;
if (IS_MFCV6(dev)) {
if (IS_MFCV6_PLUS(dev)) {
allocators[0] =
ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
allocators[1] =
@ -1761,7 +1850,8 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
if (IS_MFCV6(dev) && (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) {
if (IS_MFCV6_PLUS(dev) &&
(q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) {
if ((ctx->state == MFCINST_GOT_INST) &&
(dev->curr_ctx == ctx->num) && dev->hw_lock) {
@ -1915,7 +2005,9 @@ int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
&cfg, NULL);
} else {
if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
if ((controls[i].type == V4L2_CTRL_TYPE_MENU) ||
(controls[i].type ==
V4L2_CTRL_TYPE_INTEGER_MENU)) {
ctx->ctrls[i] = v4l2_ctrl_new_std_menu(
&ctx->ctrl_handler,
&s5p_mfc_enc_ctrl_ops, controls[i].id,

View file

@ -21,7 +21,7 @@ static struct s5p_mfc_hw_ops *s5p_mfc_ops;
void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev)
{
if (IS_MFCV6(dev)) {
if (IS_MFCV6_PLUS(dev)) {
s5p_mfc_ops = s5p_mfc_init_hw_ops_v6();
dev->warn_start = S5P_FIMV_ERR_WARNINGS_START_V6;
} else {

View file

@ -80,6 +80,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height),
S5P_FIMV_TMV_BUFFER_ALIGN_V6);
ctx->luma_dpb_size = ALIGN((mb_width * mb_height) *
S5P_FIMV_LUMA_MB_TO_PIXEL_V6,
S5P_FIMV_LUMA_DPB_BUFFER_ALIGN_V6);
@ -112,10 +113,18 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
(ctx->mv_count * ctx->mv_size);
break;
case S5P_MFC_CODEC_MPEG4_DEC:
ctx->scratch_buf_size =
S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(
mb_width,
mb_height);
if (IS_MFCV7(dev)) {
ctx->scratch_buf_size =
S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(
mb_width,
mb_height);
} else {
ctx->scratch_buf_size =
S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(
mb_width,
mb_height);
}
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
ctx->bank1.size = ctx->scratch_buf_size;
@ -179,6 +188,19 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->chroma_dpb_size + ctx->me_buffer_size));
ctx->bank2.size = 0;
break;
case S5P_MFC_CODEC_VP8_ENC:
ctx->scratch_buf_size =
S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(
mb_width,
mb_height);
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
ctx->bank1.size =
ctx->scratch_buf_size + ctx->tmv_buffer_size +
(ctx->pb_count * (ctx->luma_dpb_size +
ctx->chroma_dpb_size + ctx->me_buffer_size));
ctx->bank2.size = 0;
break;
default:
break;
}
@ -228,6 +250,7 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
break;
case S5P_MFC_CODEC_MPEG4_ENC:
case S5P_MFC_CODEC_H263_ENC:
case S5P_MFC_CODEC_VP8_ENC:
ctx->ctx.size = buf_size->other_enc_ctx;
break;
default:
@ -329,6 +352,12 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx)
ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6);
ctx->luma_size = ALIGN((mb_width * mb_height) * 256, 256);
ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256);
/* MFCv7 needs pad bytes for Luma and Chroma */
if (IS_MFCV7(ctx->dev)) {
ctx->luma_size += MFC_LUMA_PAD_BYTES_V7;
ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V7;
}
}
/* Set registers for decoding stream buffer */
@ -453,8 +482,13 @@ static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
{
struct s5p_mfc_dev *dev = ctx->dev;
WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6); /* 256B align */
WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6);
if (IS_MFCV7(dev)) {
WRITEL(y_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7);
WRITEL(c_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7);
} else {
WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6);
WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6);
}
mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr);
mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr);
@ -466,8 +500,13 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
struct s5p_mfc_dev *dev = ctx->dev;
unsigned long enc_recon_y_addr, enc_recon_c_addr;
*y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6);
*c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6);
if (IS_MFCV7(dev)) {
*y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7);
*c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7);
} else {
*y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6);
*c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6);
}
enc_recon_y_addr = READL(S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6);
enc_recon_c_addr = READL(S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6);
@ -1140,6 +1179,80 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
return 0;
}
static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_enc_params *p = &ctx->enc_params;
struct s5p_mfc_vp8_enc_params *p_vp8 = &p->codec.vp8;
unsigned int reg = 0;
unsigned int val = 0;
mfc_debug_enter();
s5p_mfc_set_enc_params(ctx);
/* pictype : number of B */
reg = READL(S5P_FIMV_E_GOP_CONFIG_V6);
reg &= ~(0x3 << 16);
reg |= ((p->num_b_frame & 0x3) << 16);
WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6);
/* profile & level */
reg = 0;
/** profile */
reg |= (0x1 << 4);
WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6);
/* rate control config. */
reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
/** macroblock level rate control */
reg &= ~(0x1 << 8);
reg |= ((p->rc_mb & 0x1) << 8);
WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
/* frame rate */
if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) {
reg = 0;
reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
reg |= p->rc_framerate_denom & 0xFFFF;
WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6);
}
/* vbv buffer size */
if (p->frame_skip_mode ==
V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
if (p->rc_frame)
WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
}
/* VP8 specific params */
reg = 0;
reg |= (p_vp8->imd_4x4 & 0x1) << 10;
switch (p_vp8->num_partitions) {
case V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION:
val = 0;
break;
case V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS:
val = 2;
break;
case V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS:
val = 4;
break;
case V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS:
val = 8;
break;
}
reg |= (val & 0xF) << 3;
reg |= (p_vp8->num_ref & 0x2);
WRITEL(reg, S5P_FIMV_E_VP8_OPTIONS_V7);
mfc_debug_leave();
return 0;
}
/* Initialize decoding */
static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
{
@ -1166,6 +1279,12 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6);
WRITEL(ctx->display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6);
}
if (IS_MFCV7(dev)) {
WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6);
reg = 0;
}
/* Setup loop filter, for decoding this is only valid for MPEG4 */
if (ctx->codec_mode == S5P_MFC_CODEC_MPEG4_DEC) {
mfc_debug(2, "Set loop filter to: %d\n",
@ -1176,7 +1295,10 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16)
reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6);
WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6);
if (IS_MFCV7(dev))
WRITEL(reg, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7);
else
WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M)
@ -1184,6 +1306,7 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
else
WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6);
/* sei parse */
WRITEL(ctx->sei_fp_parse & 0x1, S5P_FIMV_D_SEI_ENABLE_V6);
@ -1248,12 +1371,20 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_params_mpeg4(ctx);
else if (ctx->codec_mode == S5P_MFC_CODEC_H263_ENC)
s5p_mfc_set_enc_params_h263(ctx);
else if (ctx->codec_mode == S5P_MFC_CODEC_VP8_ENC)
s5p_mfc_set_enc_params_vp8(ctx);
else {
mfc_err("Unknown codec for encoding (%x).\n",
ctx->codec_mode);
return -EINVAL;
}
/* Set stride lengths */
if (IS_MFCV7(dev)) {
WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7);
WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7);
}
WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_SEQ_HEADER_V6, NULL);

View file

@ -37,6 +37,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-dv-timings.h>
#include "regs-hdmi.h"
@ -625,7 +626,7 @@ static int hdmi_s_dv_timings(struct v4l2_subdev *sd,
int i;
for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++)
if (v4l_match_dv_timings(&hdmi_timings[i].dv_timings,
if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings,
timings, 0))
break;
if (i == ARRAY_SIZE(hdmi_timings)) {

View file

@ -44,6 +44,14 @@ 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"
depends on VIDEO_DEV && SOC_CAMERA
select VIDEOBUF2_DMA_CONTIG
select SOC_CAMERA_SCALE_CROP
---help---
This is a v4l2 driver for the R-Car VIN Interface
config VIDEO_SH_MOBILE_CSI2
tristate "SuperH Mobile MIPI CSI-2 Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK

View file

@ -14,3 +14,4 @@ obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.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

View file

@ -672,7 +672,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
fmt = soc_mbus_get_fmtdesc(code);
if (!fmt) {
dev_warn(icd->parent,
"Unsupported format code #%u: %d\n", idx, code);
"Unsupported format code #%u: 0x%x\n", idx, code);
return 0;
}
@ -688,7 +688,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
xlate->host_fmt = &mx3_camera_formats[0];
xlate->code = code;
xlate++;
dev_dbg(dev, "Providing format %s using code %d\n",
dev_dbg(dev, "Providing format %s using code 0x%x\n",
mx3_camera_formats[0].name, code);
}
break;
@ -698,7 +698,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
xlate->host_fmt = &mx3_camera_formats[1];
xlate->code = code;
xlate++;
dev_dbg(dev, "Providing format %s using code %d\n",
dev_dbg(dev, "Providing format %s using code 0x%x\n",
mx3_camera_formats[1].name, code);
}
break;
@ -1144,6 +1144,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
static int mx3_camera_probe(struct platform_device *pdev)
{
struct mx3_camera_pdata *pdata = pdev->dev.platform_data;
struct mx3_camera_dev *mx3_cam;
struct resource *res;
void __iomem *base;
@ -1151,26 +1152,25 @@ static int mx3_camera_probe(struct platform_device *pdev)
struct soc_camera_host *soc_host;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
err = -ENODEV;
goto egetres;
}
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
mx3_cam = vzalloc(sizeof(*mx3_cam));
if (!pdata)
return -EINVAL;
mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL);
if (!mx3_cam) {
dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
err = -ENOMEM;
goto ealloc;
return -ENOMEM;
}
mx3_cam->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(mx3_cam->clk)) {
err = PTR_ERR(mx3_cam->clk);
goto eclkget;
}
mx3_cam->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(mx3_cam->clk))
return PTR_ERR(mx3_cam->clk);
mx3_cam->pdata = pdev->dev.platform_data;
mx3_cam->platform_flags = mx3_cam->pdata->flags;
mx3_cam->pdata = pdata;
mx3_cam->platform_flags = pdata->flags;
if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) {
/*
* Platform hasn't set available data widths. This is bad.
@ -1189,7 +1189,7 @@ static int mx3_camera_probe(struct platform_device *pdev)
if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
mx3_cam->width_flags |= 1 << 14;
mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
mx3_cam->mclk = pdata->mclk_10khz * 10000;
if (!mx3_cam->mclk) {
dev_warn(&pdev->dev,
"mclk_10khz == 0! Please, fix your platform data. "
@ -1201,13 +1201,6 @@ static int mx3_camera_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mx3_cam->capture);
spin_lock_init(&mx3_cam->lock);
base = ioremap(res->start, resource_size(res));
if (!base) {
pr_err("Couldn't map %x@%x\n", resource_size(res), res->start);
err = -ENOMEM;
goto eioremap;
}
mx3_cam->base = base;
soc_host = &mx3_cam->soc_host;
@ -1218,9 +1211,12 @@ static int mx3_camera_probe(struct platform_device *pdev)
soc_host->nr = pdev->id;
mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(mx3_cam->alloc_ctx)) {
err = PTR_ERR(mx3_cam->alloc_ctx);
goto eallocctx;
if (IS_ERR(mx3_cam->alloc_ctx))
return PTR_ERR(mx3_cam->alloc_ctx);
if (pdata->asd_sizes) {
soc_host->asd = pdata->asd;
soc_host->asd_sizes = pdata->asd_sizes;
}
err = soc_camera_host_register(soc_host);
@ -1234,14 +1230,6 @@ static int mx3_camera_probe(struct platform_device *pdev)
ecamhostreg:
vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
eallocctx:
iounmap(base);
eioremap:
clk_put(mx3_cam->clk);
eclkget:
vfree(mx3_cam);
ealloc:
egetres:
return err;
}
@ -1251,12 +1239,8 @@ static int mx3_camera_remove(struct platform_device *pdev)
struct mx3_camera_dev *mx3_cam = container_of(soc_host,
struct mx3_camera_dev, soc_host);
clk_put(mx3_cam->clk);
soc_camera_host_unregister(soc_host);
iounmap(mx3_cam->base);
/*
* The channel has either not been allocated,
* or should have been released
@ -1266,8 +1250,6 @@ static int mx3_camera_remove(struct platform_device *pdev)
vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
vfree(mx3_cam);
dmaengine_put();
return 0;
@ -1276,6 +1258,7 @@ static int mx3_camera_remove(struct platform_device *pdev)
static struct platform_driver mx3_camera_driver = {
.driver = {
.name = MX3_CAM_DRV_NAME,
.owner = THIS_MODULE,
},
.probe = mx3_camera_probe,
.remove = mx3_camera_remove,

File diff suppressed because it is too large Load diff

View file

@ -610,13 +610,12 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici)
{
struct sh_mobile_ceu_dev *pcdev = ici->priv;
int ret;
pm_runtime_get_sync(ici->v4l2_dev.dev);
pcdev->buf_total = 0;
ret = sh_mobile_ceu_soft_reset(pcdev);
sh_mobile_ceu_soft_reset(pcdev);
return 0;
}
@ -1837,9 +1836,9 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
for (j = 0; pcdev->pdata->asd_sizes[j]; j++) {
for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) {
dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n",
__func__, i, (*asd)->bus_type);
if ((*asd)->bus_type == V4L2_ASYNC_BUS_PLATFORM &&
!strncmp(name, (*asd)->match.platform.name,
__func__, i, (*asd)->match_type);
if ((*asd)->match_type == V4L2_ASYNC_MATCH_DEVNAME &&
!strncmp(name, (*asd)->match.device_name.name,
sizeof(name) - 1)) {
pcdev->csi2_asd = *asd;
break;

View file

@ -136,7 +136,7 @@ EXPORT_SYMBOL(soc_camera_power_off);
int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd)
{
/* Should not have any effect in synchronous case */
return devm_regulator_bulk_get(dev, ssdd->num_regulators,
ssdd->regulators);
}
@ -1311,6 +1311,7 @@ eusrfmt:
static int soc_camera_i2c_init(struct soc_camera_device *icd,
struct soc_camera_desc *sdesc)
{
struct soc_camera_subdev_desc *ssdd;
struct i2c_client *client;
struct soc_camera_host *ici;
struct soc_camera_host_desc *shd = &sdesc->host_desc;
@ -1333,7 +1334,21 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,
return -ENODEV;
}
shd->board_info->platform_data = &sdesc->subdev_desc;
ssdd = kzalloc(sizeof(*ssdd), GFP_KERNEL);
if (!ssdd) {
ret = -ENOMEM;
goto ealloc;
}
memcpy(ssdd, &sdesc->subdev_desc, sizeof(*ssdd));
/*
* In synchronous case we request regulators ourselves in
* soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try
* to allocate them again.
*/
ssdd->num_regulators = 0;
ssdd->regulators = NULL;
shd->board_info->platform_data = ssdd;
snprintf(clk_name, sizeof(clk_name), "%d-%04x",
shd->i2c_adapter_id, shd->board_info->addr);
@ -1359,8 +1374,10 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,
return 0;
ei2cnd:
v4l2_clk_unregister(icd->clk);
eclkreg:
icd->clk = NULL;
eclkreg:
kfree(ssdd);
ealloc:
i2c_put_adapter(adap);
return ret;
}
@ -1370,15 +1387,18 @@ static void soc_camera_i2c_free(struct soc_camera_device *icd)
struct i2c_client *client =
to_i2c_client(to_soc_camera_control(icd));
struct i2c_adapter *adap;
struct soc_camera_subdev_desc *ssdd;
icd->control = NULL;
if (icd->sasc)
return;
adap = client->adapter;
ssdd = client->dev.platform_data;
v4l2_device_unregister_subdev(i2c_get_clientdata(client));
i2c_unregister_device(client);
i2c_put_adapter(adap);
kfree(ssdd);
v4l2_clk_unregister(icd->clk);
icd->clk = NULL;
}
@ -1466,7 +1486,8 @@ static int scan_async_group(struct soc_camera_host *ici,
struct soc_camera_device *icd;
struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,};
char clk_name[V4L2_SUBDEV_NAME_SIZE];
int ret, i;
unsigned int i;
int ret;
/* First look for a sensor */
for (i = 0; i < size; i++) {
@ -1475,7 +1496,7 @@ static int scan_async_group(struct soc_camera_host *ici,
break;
}
if (i == size || asd[i]->bus_type != V4L2_ASYNC_BUS_I2C) {
if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) {
/* All useless */
dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n");
return -ENODEV;
@ -1501,7 +1522,7 @@ static int scan_async_group(struct soc_camera_host *ici,
return -ENOMEM;
}
sasc->notifier.subdev = asd;
sasc->notifier.subdevs = asd;
sasc->notifier.num_subdevs = size;
sasc->notifier.bound = soc_camera_async_bound;
sasc->notifier.unbind = soc_camera_async_unbind;
@ -1994,9 +2015,10 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev)
/*
* In the asynchronous case ssdd->num_regulators == 0 yet, so, the below
* regulator allocation is a dummy. They will be really requested later
* in soc_camera_async_bind(). Also note, that in that case regulators
* are attached to the I2C device and not to the camera platform device.
* regulator allocation is a dummy. They are actually requested by the
* subdevice driver, using soc_camera_power_init(). Also note, that in
* that case regulators are attached to the I2C device and not to the
* camera platform device.
*/
ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators,
ssdd->regulators);

View file

@ -0,0 +1,5 @@
vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o
vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
vsp1-y += vsp1_lif.o vsp1_uds.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o

View file

@ -0,0 +1,74 @@
/*
* vsp1.h -- R-Car VSP1 Driver
*
* Copyright (C) 2013 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_H__
#define __VSP1_H__
#include <linux/io.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/platform_data/vsp1.h>
#include <media/media-device.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include "vsp1_regs.h"
struct clk;
struct device;
struct vsp1_platform_data;
struct vsp1_lif;
struct vsp1_rwpf;
struct vsp1_uds;
#define VPS1_MAX_RPF 5
#define VPS1_MAX_UDS 3
#define VPS1_MAX_WPF 4
struct vsp1_device {
struct device *dev;
struct vsp1_platform_data *pdata;
void __iomem *mmio;
struct clk *clock;
struct clk *rt_clock;
struct mutex lock;
int ref_count;
struct vsp1_lif *lif;
struct vsp1_rwpf *rpf[VPS1_MAX_RPF];
struct vsp1_uds *uds[VPS1_MAX_UDS];
struct vsp1_rwpf *wpf[VPS1_MAX_WPF];
struct list_head entities;
struct v4l2_device v4l2_dev;
struct media_device media_dev;
};
struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1);
void vsp1_device_put(struct vsp1_device *vsp1);
static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg)
{
return ioread32(vsp1->mmio + reg);
}
static inline void vsp1_write(struct vsp1_device *vsp1, u32 reg, u32 data)
{
iowrite32(data, vsp1->mmio + reg);
}
#endif /* __VSP1_H__ */

View file

@ -0,0 +1,527 @@
/*
* vsp1_drv.c -- R-Car VSP1 Driver
*
* Copyright (C) 2013 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.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include "vsp1.h"
#include "vsp1_lif.h"
#include "vsp1_rwpf.h"
#include "vsp1_uds.h"
/* -----------------------------------------------------------------------------
* Interrupt Handling
*/
static irqreturn_t vsp1_irq_handler(int irq, void *data)
{
u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE;
struct vsp1_device *vsp1 = data;
irqreturn_t ret = IRQ_NONE;
unsigned int i;
for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
struct vsp1_pipeline *pipe;
u32 status;
if (wpf == NULL)
continue;
pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i));
vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
if (status & VI6_WFP_IRQ_STA_FRE) {
vsp1_pipeline_frame_end(pipe);
ret = IRQ_HANDLED;
}
}
return ret;
}
/* -----------------------------------------------------------------------------
* Entities
*/
/*
* vsp1_create_links - Create links from all sources to the given sink
*
* This function creates media links from all valid sources to the given sink
* pad. Links that would be invalid according to the VSP1 hardware capabilities
* are skipped. Those include all links
*
* - from a UDS to a UDS (UDS entities can't be chained)
* - from an entity to itself (no loops are allowed)
*/
static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink)
{
struct media_entity *entity = &sink->subdev.entity;
struct vsp1_entity *source;
unsigned int pad;
int ret;
list_for_each_entry(source, &vsp1->entities, list_dev) {
u32 flags;
if (source->type == sink->type)
continue;
if (source->type == VSP1_ENTITY_LIF ||
source->type == VSP1_ENTITY_WPF)
continue;
flags = source->type == VSP1_ENTITY_RPF &&
sink->type == VSP1_ENTITY_WPF &&
source->index == sink->index
? MEDIA_LNK_FL_ENABLED : 0;
for (pad = 0; pad < entity->num_pads; ++pad) {
if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK))
continue;
ret = media_entity_create_link(&source->subdev.entity,
source->source_pad,
entity, pad, flags);
if (ret < 0)
return ret;
if (flags & MEDIA_LNK_FL_ENABLED)
source->sink = entity;
}
}
return 0;
}
static void vsp1_destroy_entities(struct vsp1_device *vsp1)
{
struct vsp1_entity *entity;
struct vsp1_entity *next;
list_for_each_entry_safe(entity, next, &vsp1->entities, list_dev) {
list_del(&entity->list_dev);
vsp1_entity_destroy(entity);
}
v4l2_device_unregister(&vsp1->v4l2_dev);
media_device_unregister(&vsp1->media_dev);
}
static int vsp1_create_entities(struct vsp1_device *vsp1)
{
struct media_device *mdev = &vsp1->media_dev;
struct v4l2_device *vdev = &vsp1->v4l2_dev;
struct vsp1_entity *entity;
unsigned int i;
int ret;
mdev->dev = vsp1->dev;
strlcpy(mdev->model, "VSP1", sizeof(mdev->model));
snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
dev_name(mdev->dev));
ret = media_device_register(mdev);
if (ret < 0) {
dev_err(vsp1->dev, "media device registration failed (%d)\n",
ret);
return ret;
}
vdev->mdev = mdev;
ret = v4l2_device_register(vsp1->dev, vdev);
if (ret < 0) {
dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n",
ret);
goto done;
}
/* Instantiate all the entities. */
if (vsp1->pdata->features & VSP1_HAS_LIF) {
vsp1->lif = vsp1_lif_create(vsp1);
if (IS_ERR(vsp1->lif)) {
ret = PTR_ERR(vsp1->lif);
goto done;
}
list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);
}
for (i = 0; i < vsp1->pdata->rpf_count; ++i) {
struct vsp1_rwpf *rpf;
rpf = vsp1_rpf_create(vsp1, i);
if (IS_ERR(rpf)) {
ret = PTR_ERR(rpf);
goto done;
}
vsp1->rpf[i] = rpf;
list_add_tail(&rpf->entity.list_dev, &vsp1->entities);
}
for (i = 0; i < vsp1->pdata->uds_count; ++i) {
struct vsp1_uds *uds;
uds = vsp1_uds_create(vsp1, i);
if (IS_ERR(uds)) {
ret = PTR_ERR(uds);
goto done;
}
vsp1->uds[i] = uds;
list_add_tail(&uds->entity.list_dev, &vsp1->entities);
}
for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
struct vsp1_rwpf *wpf;
wpf = vsp1_wpf_create(vsp1, i);
if (IS_ERR(wpf)) {
ret = PTR_ERR(wpf);
goto done;
}
vsp1->wpf[i] = wpf;
list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
}
/* Create links. */
list_for_each_entry(entity, &vsp1->entities, list_dev) {
if (entity->type == VSP1_ENTITY_LIF ||
entity->type == VSP1_ENTITY_RPF)
continue;
ret = vsp1_create_links(vsp1, entity);
if (ret < 0)
goto done;
}
if (vsp1->pdata->features & VSP1_HAS_LIF) {
ret = media_entity_create_link(
&vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE,
&vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0);
if (ret < 0)
return ret;
}
/* Register all subdevs. */
list_for_each_entry(entity, &vsp1->entities, list_dev) {
ret = v4l2_device_register_subdev(&vsp1->v4l2_dev,
&entity->subdev);
if (ret < 0)
goto done;
}
ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
done:
if (ret < 0)
vsp1_destroy_entities(vsp1);
return ret;
}
static int vsp1_device_init(struct vsp1_device *vsp1)
{
unsigned int i;
u32 status;
/* Reset any channel that might be running. */
status = vsp1_read(vsp1, VI6_STATUS);
for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
unsigned int timeout;
if (!(status & VI6_STATUS_SYS_ACT(i)))
continue;
vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(i));
for (timeout = 10; timeout > 0; --timeout) {
status = vsp1_read(vsp1, VI6_STATUS);
if (!(status & VI6_STATUS_SYS_ACT(i)))
break;
usleep_range(1000, 2000);
}
if (!timeout) {
dev_err(vsp1->dev, "failed to reset wpf.%u\n", i);
return -ETIMEDOUT;
}
}
vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |
(8 << VI6_CLK_DCSWT_CSTRW_SHIFT));
for (i = 0; i < vsp1->pdata->rpf_count; ++i)
vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);
for (i = 0; i < vsp1->pdata->uds_count; ++i)
vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
(VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
(VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
return 0;
}
static int vsp1_clocks_enable(struct vsp1_device *vsp1)
{
int ret;
ret = clk_prepare_enable(vsp1->clock);
if (ret < 0)
return ret;
if (IS_ERR(vsp1->rt_clock))
return 0;
ret = clk_prepare_enable(vsp1->rt_clock);
if (ret < 0) {
clk_disable_unprepare(vsp1->clock);
return ret;
}
return 0;
}
static void vsp1_clocks_disable(struct vsp1_device *vsp1)
{
if (!IS_ERR(vsp1->rt_clock))
clk_disable_unprepare(vsp1->rt_clock);
clk_disable_unprepare(vsp1->clock);
}
/*
* vsp1_device_get - Acquire the VSP1 device
*
* Increment the VSP1 reference count and initialize the device if the first
* reference is taken.
*
* Return a pointer to the VSP1 device or NULL if an error occured.
*/
struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1)
{
struct vsp1_device *__vsp1 = vsp1;
int ret;
mutex_lock(&vsp1->lock);
if (vsp1->ref_count > 0)
goto done;
ret = vsp1_clocks_enable(vsp1);
if (ret < 0) {
__vsp1 = NULL;
goto done;
}
ret = vsp1_device_init(vsp1);
if (ret < 0) {
vsp1_clocks_disable(vsp1);
__vsp1 = NULL;
goto done;
}
done:
if (__vsp1)
vsp1->ref_count++;
mutex_unlock(&vsp1->lock);
return __vsp1;
}
/*
* vsp1_device_put - Release the VSP1 device
*
* Decrement the VSP1 reference count and cleanup the device if the last
* reference is released.
*/
void vsp1_device_put(struct vsp1_device *vsp1)
{
mutex_lock(&vsp1->lock);
if (--vsp1->ref_count == 0)
vsp1_clocks_disable(vsp1);
mutex_unlock(&vsp1->lock);
}
/* -----------------------------------------------------------------------------
* Power Management
*/
#ifdef CONFIG_PM_SLEEP
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_clocks_disable(vsp1);
return 0;
}
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)
return 0;
return vsp1_clocks_enable(vsp1);
}
#endif
static const struct dev_pm_ops vsp1_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
};
/* -----------------------------------------------------------------------------
* Platform Driver
*/
static struct vsp1_platform_data *
vsp1_get_platform_data(struct platform_device *pdev)
{
struct vsp1_platform_data *pdata = pdev->dev.platform_data;
if (pdata == NULL) {
dev_err(&pdev->dev, "missing platform data\n");
return NULL;
}
if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) {
dev_err(&pdev->dev, "invalid number of RPF (%u)\n",
pdata->rpf_count);
return NULL;
}
if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) {
dev_err(&pdev->dev, "invalid number of UDS (%u)\n",
pdata->uds_count);
return NULL;
}
if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) {
dev_err(&pdev->dev, "invalid number of WPF (%u)\n",
pdata->wpf_count);
return NULL;
}
return pdata;
}
static int vsp1_probe(struct platform_device *pdev)
{
struct vsp1_device *vsp1;
struct resource *irq;
struct resource *io;
int ret;
vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);
if (vsp1 == NULL)
return -ENOMEM;
vsp1->dev = &pdev->dev;
mutex_init(&vsp1->lock);
INIT_LIST_HEAD(&vsp1->entities);
vsp1->pdata = vsp1_get_platform_data(pdev);
if (vsp1->pdata == NULL)
return -ENODEV;
/* I/O, IRQ and clock resources */
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);
}
/* The RT clock is optional */
vsp1->rt_clock = devm_clk_get(&pdev->dev, "rt");
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq) {
dev_err(&pdev->dev, "missing IRQ\n");
return -EINVAL;
}
ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler,
IRQF_SHARED, dev_name(&pdev->dev), vsp1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request IRQ\n");
return ret;
}
/* Instanciate entities */
ret = vsp1_create_entities(vsp1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to create entities\n");
return ret;
}
platform_set_drvdata(pdev, vsp1);
return 0;
}
static int vsp1_remove(struct platform_device *pdev)
{
struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
vsp1_destroy_entities(vsp1);
return 0;
}
static struct platform_driver vsp1_platform_driver = {
.probe = vsp1_probe,
.remove = vsp1_remove,
.driver = {
.owner = THIS_MODULE,
.name = "vsp1",
.pm = &vsp1_pm_ops,
},
};
module_platform_driver(vsp1_platform_driver);
MODULE_ALIAS("vsp1");
MODULE_AUTHOR("Laurent Pinchart <[email protected]>");
MODULE_DESCRIPTION("Renesas VSP1 Driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,181 @@
/*
* vsp1_entity.c -- R-Car VSP1 Base Entity
*
* Copyright (C) 2013 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.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_entity.h"
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
struct v4l2_mbus_framefmt *
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
struct v4l2_subdev_fh *fh,
unsigned int pad, u32 which)
{
switch (which) {
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_format(fh, pad);
case V4L2_SUBDEV_FORMAT_ACTIVE:
return &entity->formats[pad];
default:
return NULL;
}
}
/*
* vsp1_entity_init_formats - Initialize formats on all pads
* @subdev: V4L2 subdevice
* @fh: V4L2 subdev file handle
*
* Initialize all pad formats with default values. If fh is not NULL, try
* formats are initialized on the file handle. Otherwise active formats are
* initialized on the device.
*/
void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh)
{
struct v4l2_subdev_format format;
unsigned int pad;
for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) {
memset(&format, 0, sizeof(format));
format.pad = pad;
format.which = fh ? V4L2_SUBDEV_FORMAT_TRY
: V4L2_SUBDEV_FORMAT_ACTIVE;
v4l2_subdev_call(subdev, pad, set_fmt, fh, &format);
}
}
static int vsp1_entity_open(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh)
{
vsp1_entity_init_formats(subdev, fh);
return 0;
}
const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = {
.open = vsp1_entity_open,
};
/* -----------------------------------------------------------------------------
* Media Operations
*/
static int vsp1_entity_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
struct vsp1_entity *source;
if (!(local->flags & MEDIA_PAD_FL_SOURCE))
return 0;
source = container_of(local->entity, struct vsp1_entity, subdev.entity);
if (!source->route)
return 0;
if (flags & MEDIA_LNK_FL_ENABLED) {
if (source->sink)
return -EBUSY;
source->sink = remote->entity;
} else {
source->sink = NULL;
}
return 0;
}
const struct media_entity_operations vsp1_media_ops = {
.link_setup = vsp1_entity_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
/* -----------------------------------------------------------------------------
* Initialization
*/
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
unsigned int num_pads)
{
static const struct {
unsigned int id;
unsigned int reg;
} routes[] = {
{ VI6_DPR_NODE_LIF, 0 },
{ VI6_DPR_NODE_RPF(0), VI6_DPR_RPF_ROUTE(0) },
{ VI6_DPR_NODE_RPF(1), VI6_DPR_RPF_ROUTE(1) },
{ VI6_DPR_NODE_RPF(2), VI6_DPR_RPF_ROUTE(2) },
{ VI6_DPR_NODE_RPF(3), VI6_DPR_RPF_ROUTE(3) },
{ VI6_DPR_NODE_RPF(4), VI6_DPR_RPF_ROUTE(4) },
{ VI6_DPR_NODE_UDS(0), VI6_DPR_UDS_ROUTE(0) },
{ VI6_DPR_NODE_UDS(1), VI6_DPR_UDS_ROUTE(1) },
{ VI6_DPR_NODE_UDS(2), VI6_DPR_UDS_ROUTE(2) },
{ VI6_DPR_NODE_WPF(0), 0 },
{ VI6_DPR_NODE_WPF(1), 0 },
{ VI6_DPR_NODE_WPF(2), 0 },
{ VI6_DPR_NODE_WPF(3), 0 },
};
unsigned int i;
for (i = 0; i < ARRAY_SIZE(routes); ++i) {
if (routes[i].id == entity->id) {
entity->route = routes[i].reg;
break;
}
}
if (i == ARRAY_SIZE(routes))
return -EINVAL;
entity->vsp1 = vsp1;
entity->source_pad = num_pads - 1;
/* Allocate formats and pads. */
entity->formats = devm_kzalloc(vsp1->dev,
num_pads * sizeof(*entity->formats),
GFP_KERNEL);
if (entity->formats == NULL)
return -ENOMEM;
entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads),
GFP_KERNEL);
if (entity->pads == NULL)
return -ENOMEM;
/* Initialize pads. */
for (i = 0; i < num_pads - 1; ++i)
entity->pads[i].flags = MEDIA_PAD_FL_SINK;
entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE;
/* Initialize the media entity. */
return media_entity_init(&entity->subdev.entity, num_pads,
entity->pads, 0);
}
void vsp1_entity_destroy(struct vsp1_entity *entity)
{
media_entity_cleanup(&entity->subdev.entity);
}

View file

@ -0,0 +1,68 @@
/*
* vsp1_entity.h -- R-Car VSP1 Base Entity
*
* Copyright (C) 2013 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_ENTITY_H__
#define __VSP1_ENTITY_H__
#include <linux/list.h>
#include <media/v4l2-subdev.h>
struct vsp1_device;
enum vsp1_entity_type {
VSP1_ENTITY_LIF,
VSP1_ENTITY_RPF,
VSP1_ENTITY_UDS,
VSP1_ENTITY_WPF,
};
struct vsp1_entity {
struct vsp1_device *vsp1;
enum vsp1_entity_type type;
unsigned int index;
unsigned int id;
unsigned int route;
struct list_head list_dev;
struct list_head list_pipe;
struct media_pad *pads;
unsigned int source_pad;
struct media_entity *sink;
struct v4l2_subdev subdev;
struct v4l2_mbus_framefmt *formats;
};
static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_entity, subdev);
}
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
unsigned int num_pads);
void vsp1_entity_destroy(struct vsp1_entity *entity);
extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops;
extern const struct media_entity_operations vsp1_media_ops;
struct v4l2_mbus_framefmt *
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
struct v4l2_subdev_fh *fh,
unsigned int pad, u32 which);
void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh);
#endif /* __VSP1_ENTITY_H__ */

View file

@ -0,0 +1,238 @@
/*
* vsp1_lif.c -- R-Car VSP1 LCD Controller Interface
*
* Copyright (C) 2013 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.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_lif.h"
#define LIF_MIN_SIZE 2U
#define LIF_MAX_SIZE 2048U
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_lif_read(struct vsp1_lif *lif, u32 reg)
{
return vsp1_read(lif->entity.vsp1, reg);
}
static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data)
{
vsp1_write(lif->entity.vsp1, reg, data);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int lif_s_stream(struct v4l2_subdev *subdev, int enable)
{
const struct v4l2_mbus_framefmt *format;
struct vsp1_lif *lif = to_lif(subdev);
unsigned int hbth = 1300;
unsigned int obth = 400;
unsigned int lbth = 200;
if (!enable) {
vsp1_lif_write(lif, VI6_LIF_CTRL, 0);
return 0;
}
format = &lif->entity.formats[LIF_PAD_SOURCE];
obth = min(obth, (format->width + 1) / 2 * format->height - 4);
vsp1_lif_write(lif, VI6_LIF_CSBTH,
(hbth << VI6_LIF_CSBTH_HBTH_SHIFT) |
(lbth << VI6_LIF_CSBTH_LBTH_SHIFT));
vsp1_lif_write(lif, VI6_LIF_CTRL,
(obth << VI6_LIF_CTRL_OBTH_SHIFT) |
(format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) |
VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
static int lif_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
V4L2_MBUS_FMT_ARGB8888_1X32,
V4L2_MBUS_FMT_AYUV8_1X32,
};
if (code->pad == LIF_PAD_SINK) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
struct v4l2_mbus_framefmt *format;
/* The LIF can't perform format conversion, the sink format is
* always identical to the source format.
*/
if (code->index)
return -EINVAL;
format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
code->code = format->code;
}
return 0;
}
static int lif_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct v4l2_mbus_framefmt *format;
format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == LIF_PAD_SINK) {
fse->min_width = LIF_MIN_SIZE;
fse->max_width = LIF_MAX_SIZE;
fse->min_height = LIF_MIN_SIZE;
fse->max_height = LIF_MAX_SIZE;
} else {
fse->min_width = format->width;
fse->max_width = format->width;
fse->min_height = format->height;
fse->max_height = format->height;
}
return 0;
}
static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lif *lif = to_lif(subdev);
fmt->format = *vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
fmt->which);
return 0;
}
static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lif *lif = to_lif(subdev);
struct v4l2_mbus_framefmt *format;
/* Default to YUV if the requested format is not supported. */
if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
fmt->which);
if (fmt->pad == LIF_PAD_SOURCE) {
/* The LIF source format is always identical to its sink
* format.
*/
fmt->format = *format;
return 0;
}
format->code = fmt->format.code;
format->width = clamp_t(unsigned int, fmt->format.width,
LIF_MIN_SIZE, LIF_MAX_SIZE);
format->height = clamp_t(unsigned int, fmt->format.height,
LIF_MIN_SIZE, LIF_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(&lif->entity, fh, LIF_PAD_SOURCE,
fmt->which);
*format = fmt->format;
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops lif_video_ops = {
.s_stream = lif_s_stream,
};
static struct v4l2_subdev_pad_ops lif_pad_ops = {
.enum_mbus_code = lif_enum_mbus_code,
.enum_frame_size = lif_enum_frame_size,
.get_fmt = lif_get_format,
.set_fmt = lif_set_format,
};
static struct v4l2_subdev_ops lif_ops = {
.video = &lif_video_ops,
.pad = &lif_pad_ops,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
{
struct v4l2_subdev *subdev;
struct vsp1_lif *lif;
int ret;
lif = devm_kzalloc(vsp1->dev, sizeof(*lif), GFP_KERNEL);
if (lif == NULL)
return ERR_PTR(-ENOMEM);
lif->entity.type = VSP1_ENTITY_LIF;
lif->entity.id = VI6_DPR_NODE_LIF;
ret = vsp1_entity_init(vsp1, &lif->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &lif->entity.subdev;
v4l2_subdev_init(subdev, &lif_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s lif",
dev_name(vsp1->dev));
v4l2_set_subdevdata(subdev, lif);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
return lif;
}

View file

@ -0,0 +1,37 @@
/*
* vsp1_lif.h -- R-Car VSP1 LCD Controller Interface
*
* Copyright (C) 2013 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_LIF_H__
#define __VSP1_LIF_H__
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
struct vsp1_device;
#define LIF_PAD_SINK 0
#define LIF_PAD_SOURCE 1
struct vsp1_lif {
struct vsp1_entity entity;
};
static inline struct vsp1_lif *to_lif(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_lif, entity.subdev);
}
struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1);
#endif /* __VSP1_LIF_H__ */

View file

@ -0,0 +1,581 @@
/*
* vsp1_regs.h -- R-Car VSP1 Registers Definitions
*
* Copyright (C) 2013 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 version 2
* as published by the Free Software Foundation.
*/
#ifndef __VSP1_REGS_H__
#define __VSP1_REGS_H__
/* -----------------------------------------------------------------------------
* General Control Registers
*/
#define VI6_CMD(n) (0x0000 + (n) * 4)
#define VI6_CMD_STRCMD (1 << 0)
#define VI6_CLK_DCSWT 0x0018
#define VI6_CLK_DCSWT_CSTPW_MASK (0xff << 8)
#define VI6_CLK_DCSWT_CSTPW_SHIFT 8
#define VI6_CLK_DCSWT_CSTRW_MASK (0xff << 0)
#define VI6_CLK_DCSWT_CSTRW_SHIFT 0
#define VI6_SRESET 0x0028
#define VI6_SRESET_SRTS(n) (1 << (n))
#define VI6_STATUS 0x0038
#define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8))
#define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12)
#define VI6_WFP_IRQ_ENB_DFEE (1 << 1)
#define VI6_WFP_IRQ_ENB_FREE (1 << 0)
#define VI6_WPF_IRQ_STA(n) (0x004c + (n) * 12)
#define VI6_WFP_IRQ_STA_DFE (1 << 1)
#define VI6_WFP_IRQ_STA_FRE (1 << 0)
#define VI6_DISP_IRQ_ENB 0x0078
#define VI6_DISP_IRQ_ENB_DSTE (1 << 8)
#define VI6_DISP_IRQ_ENB_MAEE (1 << 5)
#define VI6_DISP_IRQ_ENB_LNEE(n) (1 << ((n) + 4))
#define VI6_DISP_IRQ_STA 0x007c
#define VI6_DISP_IRQ_STA_DSE (1 << 8)
#define VI6_DISP_IRQ_STA_MAE (1 << 5)
#define VI6_DISP_IRQ_STA_LNE(n) (1 << ((n) + 4))
#define VI6_WPF_LINE_COUNT(n) (0x0084 + (n) * 4)
#define VI6_WPF_LINE_COUNT_MASK (0x1fffff << 0)
/* -----------------------------------------------------------------------------
* Display List Control Registers
*/
#define VI6_DL_CTRL 0x0100
#define VI6_DL_CTRL_AR_WAIT_MASK (0xffff << 16)
#define VI6_DL_CTRL_AR_WAIT_SHIFT 16
#define VI6_DL_CTRL_DC2 (1 << 12)
#define VI6_DL_CTRL_DC1 (1 << 8)
#define VI6_DL_CTRL_DC0 (1 << 4)
#define VI6_DL_CTRL_CFM0 (1 << 2)
#define VI6_DL_CTRL_NH0 (1 << 1)
#define VI6_DL_CTRL_DLE (1 << 0)
#define VI6_DL_HDR_ADDR(n) (0x0104 + (n) * 4)
#define VI6_DL_SWAP 0x0114
#define VI6_DL_SWAP_LWS (1 << 2)
#define VI6_DL_SWAP_WDS (1 << 1)
#define VI6_DL_SWAP_BTS (1 << 0)
#define VI6_DL_EXT_CTRL 0x011c
#define VI6_DL_EXT_CTRL_NWE (1 << 16)
#define VI6_DL_EXT_CTRL_POLINT_MASK (0x3f << 8)
#define VI6_DL_EXT_CTRL_POLINT_SHIFT 8
#define VI6_DL_EXT_CTRL_DLPRI (1 << 5)
#define VI6_DL_EXT_CTRL_EXPRI (1 << 4)
#define VI6_DL_EXT_CTRL_EXT (1 << 0)
#define VI6_DL_BODY_SIZE 0x0120
#define VI6_DL_BODY_SIZE_UPD (1 << 24)
#define VI6_DL_BODY_SIZE_BS_MASK (0x1ffff << 0)
#define VI6_DL_BODY_SIZE_BS_SHIFT 0
/* -----------------------------------------------------------------------------
* RPF Control Registers
*/
#define VI6_RPF_OFFSET 0x100
#define VI6_RPF_SRC_BSIZE 0x0300
#define VI6_RPF_SRC_BSIZE_BHSIZE_MASK (0x1fff << 16)
#define VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT 16
#define VI6_RPF_SRC_BSIZE_BVSIZE_MASK (0x1fff << 0)
#define VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT 0
#define VI6_RPF_SRC_ESIZE 0x0304
#define VI6_RPF_SRC_ESIZE_EHSIZE_MASK (0x1fff << 16)
#define VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT 16
#define VI6_RPF_SRC_ESIZE_EVSIZE_MASK (0x1fff << 0)
#define VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT 0
#define VI6_RPF_INFMT 0x0308
#define VI6_RPF_INFMT_VIR (1 << 28)
#define VI6_RPF_INFMT_CIPM (1 << 16)
#define VI6_RPF_INFMT_SPYCS (1 << 15)
#define VI6_RPF_INFMT_SPUVS (1 << 14)
#define VI6_RPF_INFMT_CEXT_ZERO (0 << 12)
#define VI6_RPF_INFMT_CEXT_EXT (1 << 12)
#define VI6_RPF_INFMT_CEXT_ONE (2 << 12)
#define VI6_RPF_INFMT_CEXT_MASK (3 << 12)
#define VI6_RPF_INFMT_RDTM_BT601 (0 << 9)
#define VI6_RPF_INFMT_RDTM_BT601_EXT (1 << 9)
#define VI6_RPF_INFMT_RDTM_BT709 (2 << 9)
#define VI6_RPF_INFMT_RDTM_BT709_EXT (3 << 9)
#define VI6_RPF_INFMT_RDTM_MASK (7 << 9)
#define VI6_RPF_INFMT_CSC (1 << 8)
#define VI6_RPF_INFMT_RDFMT_MASK (0x7f << 0)
#define VI6_RPF_INFMT_RDFMT_SHIFT 0
#define VI6_RPF_DSWAP 0x030c
#define VI6_RPF_DSWAP_A_LLS (1 << 11)
#define VI6_RPF_DSWAP_A_LWS (1 << 10)
#define VI6_RPF_DSWAP_A_WDS (1 << 9)
#define VI6_RPF_DSWAP_A_BTS (1 << 8)
#define VI6_RPF_DSWAP_P_LLS (1 << 3)
#define VI6_RPF_DSWAP_P_LWS (1 << 2)
#define VI6_RPF_DSWAP_P_WDS (1 << 1)
#define VI6_RPF_DSWAP_P_BTS (1 << 0)
#define VI6_RPF_LOC 0x0310
#define VI6_RPF_LOC_HCOORD_MASK (0x1fff << 16)
#define VI6_RPF_LOC_HCOORD_SHIFT 16
#define VI6_RPF_LOC_VCOORD_MASK (0x1fff << 0)
#define VI6_RPF_LOC_VCOORD_SHIFT 0
#define VI6_RPF_ALPH_SEL 0x0314
#define VI6_RPF_ALPH_SEL_ASEL_PACKED (0 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_8B_PLANE (1 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_SELECT (2 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_1B_PLANE (3 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_FIXED (4 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_MASK (7 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_SHIFT 28
#define VI6_RPF_ALPH_SEL_IROP_MASK (0xf << 24)
#define VI6_RPF_ALPH_SEL_IROP_SHIFT 24
#define VI6_RPF_ALPH_SEL_BSEL (1 << 23)
#define VI6_RPF_ALPH_SEL_AEXT_ZERO (0 << 18)
#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_VRTCOL_SET 0x0318
#define VI6_RPF_VRTCOL_SET_LAYA_MASK (0xff << 24)
#define VI6_RPF_VRTCOL_SET_LAYA_SHIFT 24
#define VI6_RPF_VRTCOL_SET_LAYR_MASK (0xff << 16)
#define VI6_RPF_VRTCOL_SET_LAYR_SHIFT 16
#define VI6_RPF_VRTCOL_SET_LAYG_MASK (0xff << 8)
#define VI6_RPF_VRTCOL_SET_LAYG_SHIFT 8
#define VI6_RPF_VRTCOL_SET_LAYB_MASK (0xff << 0)
#define VI6_RPF_VRTCOL_SET_LAYB_SHIFT 0
#define VI6_RPF_MSK_CTRL 0x031c
#define VI6_RPF_MSK_CTRL_MSK_EN (1 << 24)
#define VI6_RPF_MSK_CTRL_MGR_MASK (0xff << 16)
#define VI6_RPF_MSK_CTRL_MGR_SHIFT 16
#define VI6_RPF_MSK_CTRL_MGG_MASK (0xff << 8)
#define VI6_RPF_MSK_CTRL_MGG_SHIFT 8
#define VI6_RPF_MSK_CTRL_MGB_MASK (0xff << 0)
#define VI6_RPF_MSK_CTRL_MGB_SHIFT 0
#define VI6_RPF_MSK_SET0 0x0320
#define VI6_RPF_MSK_SET1 0x0324
#define VI6_RPF_MSK_SET_MSA_MASK (0xff << 24)
#define VI6_RPF_MSK_SET_MSA_SHIFT 24
#define VI6_RPF_MSK_SET_MSR_MASK (0xff << 16)
#define VI6_RPF_MSK_SET_MSR_SHIFT 16
#define VI6_RPF_MSK_SET_MSG_MASK (0xff << 8)
#define VI6_RPF_MSK_SET_MSG_SHIFT 8
#define VI6_RPF_MSK_SET_MSB_MASK (0xff << 0)
#define VI6_RPF_MSK_SET_MSB_SHIFT 0
#define VI6_RPF_CKEY_CTRL 0x0328
#define VI6_RPF_CKEY_CTRL_CV (1 << 4)
#define VI6_RPF_CKEY_CTRL_SAPE1 (1 << 1)
#define VI6_RPF_CKEY_CTRL_SAPE0 (1 << 0)
#define VI6_RPF_CKEY_SET0 0x032c
#define VI6_RPF_CKEY_SET1 0x0330
#define VI6_RPF_CKEY_SET_AP_MASK (0xff << 24)
#define VI6_RPF_CKEY_SET_AP_SHIFT 24
#define VI6_RPF_CKEY_SET_R_MASK (0xff << 16)
#define VI6_RPF_CKEY_SET_R_SHIFT 16
#define VI6_RPF_CKEY_SET_GY_MASK (0xff << 8)
#define VI6_RPF_CKEY_SET_GY_SHIFT 8
#define VI6_RPF_CKEY_SET_B_MASK (0xff << 0)
#define VI6_RPF_CKEY_SET_B_SHIFT 0
#define VI6_RPF_SRCM_PSTRIDE 0x0334
#define VI6_RPF_SRCM_PSTRIDE_Y_SHIFT 16
#define VI6_RPF_SRCM_PSTRIDE_C_SHIFT 0
#define VI6_RPF_SRCM_ASTRIDE 0x0338
#define VI6_RPF_SRCM_PSTRIDE_A_SHIFT 0
#define VI6_RPF_SRCM_ADDR_Y 0x033c
#define VI6_RPF_SRCM_ADDR_C0 0x0340
#define VI6_RPF_SRCM_ADDR_C1 0x0344
#define VI6_RPF_SRCM_ADDR_AI 0x0348
/* -----------------------------------------------------------------------------
* WPF Control Registers
*/
#define VI6_WPF_OFFSET 0x100
#define VI6_WPF_SRCRPF 0x1000
#define VI6_WPF_SRCRPF_VIRACT_DIS (0 << 28)
#define VI6_WPF_SRCRPF_VIRACT_SUB (1 << 28)
#define VI6_WPF_SRCRPF_VIRACT_MST (2 << 28)
#define VI6_WPF_SRCRPF_VIRACT_MASK (3 << 28)
#define VI6_WPF_SRCRPF_RPF_ACT_DIS(n) (0 << ((n) * 2))
#define VI6_WPF_SRCRPF_RPF_ACT_SUB(n) (1 << ((n) * 2))
#define VI6_WPF_SRCRPF_RPF_ACT_MST(n) (2 << ((n) * 2))
#define VI6_WPF_SRCRPF_RPF_ACT_MASK(n) (3 << ((n) * 2))
#define VI6_WPF_HSZCLIP 0x1004
#define VI6_WPF_VSZCLIP 0x1008
#define VI6_WPF_SZCLIP_EN (1 << 28)
#define VI6_WPF_SZCLIP_OFST_MASK (0xff << 16)
#define VI6_WPF_SZCLIP_OFST_SHIFT 16
#define VI6_WPF_SZCLIP_SIZE_MASK (0x1fff << 0)
#define VI6_WPF_SZCLIP_SIZE_SHIFT 0
#define VI6_WPF_OUTFMT 0x100c
#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_FLP (1 << 16)
#define VI6_WPF_OUTFMT_SPYCS (1 << 15)
#define VI6_WPF_OUTFMT_SPUVS (1 << 14)
#define VI6_WPF_OUTFMT_DITH_DIS (0 << 12)
#define VI6_WPF_OUTFMT_DITH_EN (3 << 12)
#define VI6_WPF_OUTFMT_DITH_MASK (3 << 12)
#define VI6_WPF_OUTFMT_WRTM_BT601 (0 << 9)
#define VI6_WPF_OUTFMT_WRTM_BT601_EXT (1 << 9)
#define VI6_WPF_OUTFMT_WRTM_BT709 (2 << 9)
#define VI6_WPF_OUTFMT_WRTM_BT709_EXT (3 << 9)
#define VI6_WPF_OUTFMT_WRTM_MASK (7 << 9)
#define VI6_WPF_OUTFMT_CSC (1 << 8)
#define VI6_WPF_OUTFMT_WRFMT_MASK (0x7f << 0)
#define VI6_WPF_OUTFMT_WRFMT_SHIFT 0
#define VI6_WPF_DSWAP 0x1010
#define VI6_WPF_DSWAP_P_LLS (1 << 3)
#define VI6_WPF_DSWAP_P_LWS (1 << 2)
#define VI6_WPF_DSWAP_P_WDS (1 << 1)
#define VI6_WPF_DSWAP_P_BTS (1 << 0)
#define VI6_WPF_RNDCTRL 0x1014
#define VI6_WPF_RNDCTRL_CBRM (1 << 28)
#define VI6_WPF_RNDCTRL_ABRM_TRUNC (0 << 24)
#define VI6_WPF_RNDCTRL_ABRM_ROUND (1 << 24)
#define VI6_WPF_RNDCTRL_ABRM_THRESH (2 << 24)
#define VI6_WPF_RNDCTRL_ABRM_MASK (3 << 24)
#define VI6_WPF_RNDCTRL_ATHRESH_MASK (0xff << 16)
#define VI6_WPF_RNDCTRL_ATHRESH_SHIFT 16
#define VI6_WPF_RNDCTRL_CLMD_FULL (0 << 12)
#define VI6_WPF_RNDCTRL_CLMD_CLIP (1 << 12)
#define VI6_WPF_RNDCTRL_CLMD_EXT (2 << 12)
#define VI6_WPF_RNDCTRL_CLMD_MASK (3 << 12)
#define VI6_WPF_DSTM_STRIDE_Y 0x101c
#define VI6_WPF_DSTM_STRIDE_C 0x1020
#define VI6_WPF_DSTM_ADDR_Y 0x1024
#define VI6_WPF_DSTM_ADDR_C0 0x1028
#define VI6_WPF_DSTM_ADDR_C1 0x102c
#define VI6_WPF_WRBCK_CTRL 0x1034
#define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0)
/* -----------------------------------------------------------------------------
* DPR Control Registers
*/
#define VI6_DPR_RPF_ROUTE(n) (0x2000 + (n) * 4)
#define VI6_DPR_WPF_FPORCH(n) (0x2014 + (n) * 4)
#define VI6_DPR_WPF_FPORCH_FP_WPFN (5 << 8)
#define VI6_DPR_SRU_ROUTE 0x2024
#define VI6_DPR_UDS_ROUTE(n) (0x2028 + (n) * 4)
#define VI6_DPR_LUT_ROUTE 0x203c
#define VI6_DPR_CLU_ROUTE 0x2040
#define VI6_DPR_HST_ROUTE 0x2044
#define VI6_DPR_HSI_ROUTE 0x2048
#define VI6_DPR_BRU_ROUTE 0x204c
#define VI6_DPR_ROUTE_FXA_MASK (0xff << 8)
#define VI6_DPR_ROUTE_FXA_SHIFT 16
#define VI6_DPR_ROUTE_FP_MASK (0xff << 8)
#define VI6_DPR_ROUTE_FP_SHIFT 8
#define VI6_DPR_ROUTE_RT_MASK (0x3f << 0)
#define VI6_DPR_ROUTE_RT_SHIFT 0
#define VI6_DPR_HGO_SMPPT 0x2050
#define VI6_DPR_HGT_SMPPT 0x2054
#define VI6_DPR_SMPPT_TGW_MASK (7 << 8)
#define VI6_DPR_SMPPT_TGW_SHIFT 8
#define VI6_DPR_SMPPT_PT_MASK (0x3f << 0)
#define VI6_DPR_SMPPT_PT_SHIFT 0
#define VI6_DPR_NODE_RPF(n) (n)
#define VI6_DPR_NODE_SRU 16
#define VI6_DPR_NODE_UDS(n) (17 + (n))
#define VI6_DPR_NODE_LUT 22
#define VI6_DPR_NODE_BRU_IN(n) (23 + (n))
#define VI6_DPR_NODE_BRU_OUT 27
#define VI6_DPR_NODE_CLU 29
#define VI6_DPR_NODE_HST 30
#define VI6_DPR_NODE_HSI 31
#define VI6_DPR_NODE_LIF 55
#define VI6_DPR_NODE_WPF(n) (56 + (n))
#define VI6_DPR_NODE_UNUSED 63
/* -----------------------------------------------------------------------------
* SRU Control Registers
*/
#define VI6_SRU_CTRL0 0x2200
#define VI6_SRU_CTRL1 0x2204
#define VI6_SRU_CTRL2 0x2208
/* -----------------------------------------------------------------------------
* UDS Control Registers
*/
#define VI6_UDS_OFFSET 0x100
#define VI6_UDS_CTRL 0x2300
#define VI6_UDS_CTRL_AMD (1 << 30)
#define VI6_UDS_CTRL_FMD (1 << 29)
#define VI6_UDS_CTRL_BLADV (1 << 28)
#define VI6_UDS_CTRL_AON (1 << 25)
#define VI6_UDS_CTRL_ATHON (1 << 24)
#define VI6_UDS_CTRL_BC (1 << 20)
#define VI6_UDS_CTRL_NE_A (1 << 19)
#define VI6_UDS_CTRL_NE_RCR (1 << 18)
#define VI6_UDS_CTRL_NE_GY (1 << 17)
#define VI6_UDS_CTRL_NE_BCB (1 << 16)
#define VI6_UDS_CTRL_TDIPC (1 << 1)
#define VI6_UDS_SCALE 0x2304
#define VI6_UDS_SCALE_HMANT_MASK (0xf << 28)
#define VI6_UDS_SCALE_HMANT_SHIFT 28
#define VI6_UDS_SCALE_HFRAC_MASK (0xfff << 16)
#define VI6_UDS_SCALE_HFRAC_SHIFT 16
#define VI6_UDS_SCALE_VMANT_MASK (0xf << 12)
#define VI6_UDS_SCALE_VMANT_SHIFT 12
#define VI6_UDS_SCALE_VFRAC_MASK (0xfff << 0)
#define VI6_UDS_SCALE_VFRAC_SHIFT 0
#define VI6_UDS_ALPTH 0x2308
#define VI6_UDS_ALPTH_TH1_MASK (0xff << 8)
#define VI6_UDS_ALPTH_TH1_SHIFT 8
#define VI6_UDS_ALPTH_TH0_MASK (0xff << 0)
#define VI6_UDS_ALPTH_TH0_SHIFT 0
#define VI6_UDS_ALPVAL 0x230c
#define VI6_UDS_ALPVAL_VAL2_MASK (0xff << 16)
#define VI6_UDS_ALPVAL_VAL2_SHIFT 16
#define VI6_UDS_ALPVAL_VAL1_MASK (0xff << 8)
#define VI6_UDS_ALPVAL_VAL1_SHIFT 8
#define VI6_UDS_ALPVAL_VAL0_MASK (0xff << 0)
#define VI6_UDS_ALPVAL_VAL0_SHIFT 0
#define VI6_UDS_PASS_BWIDTH 0x2310
#define VI6_UDS_PASS_BWIDTH_H_MASK (0x7f << 16)
#define VI6_UDS_PASS_BWIDTH_H_SHIFT 16
#define VI6_UDS_PASS_BWIDTH_V_MASK (0x7f << 0)
#define VI6_UDS_PASS_BWIDTH_V_SHIFT 0
#define VI6_UDS_IPC 0x2318
#define VI6_UDS_IPC_FIELD (1 << 27)
#define VI6_UDS_IPC_VEDP_MASK (0xfff << 0)
#define VI6_UDS_IPC_VEDP_SHIFT 0
#define VI6_UDS_CLIP_SIZE 0x2324
#define VI6_UDS_CLIP_SIZE_HSIZE_MASK (0x1fff << 16)
#define VI6_UDS_CLIP_SIZE_HSIZE_SHIFT 16
#define VI6_UDS_CLIP_SIZE_VSIZE_MASK (0x1fff << 0)
#define VI6_UDS_CLIP_SIZE_VSIZE_SHIFT 0
#define VI6_UDS_FILL_COLOR 0x2328
#define VI6_UDS_FILL_COLOR_RFILC_MASK (0xff << 16)
#define VI6_UDS_FILL_COLOR_RFILC_SHIFT 16
#define VI6_UDS_FILL_COLOR_GFILC_MASK (0xff << 8)
#define VI6_UDS_FILL_COLOR_GFILC_SHIFT 8
#define VI6_UDS_FILL_COLOR_BFILC_MASK (0xff << 0)
#define VI6_UDS_FILL_COLOR_BFILC_SHIFT 0
/* -----------------------------------------------------------------------------
* LUT Control Registers
*/
#define VI6_LUT_CTRL 0x2800
/* -----------------------------------------------------------------------------
* CLU Control Registers
*/
#define VI6_CLU_CTRL 0x2900
/* -----------------------------------------------------------------------------
* HST Control Registers
*/
#define VI6_HST_CTRL 0x2a00
/* -----------------------------------------------------------------------------
* HSI Control Registers
*/
#define VI6_HSI_CTRL 0x2b00
/* -----------------------------------------------------------------------------
* BRU Control Registers
*/
#define VI6_BRU_INCTRL 0x2c00
#define VI6_BRU_VIRRPF_SIZE 0x2c04
#define VI6_BRU_VIRRPF_LOC 0x2c08
#define VI6_BRU_VIRRPF_COL 0x2c0c
#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8)
#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8)
#define VI6_BRU_ROP 0x2c30
/* -----------------------------------------------------------------------------
* HGO Control Registers
*/
#define VI6_HGO_OFFSET 0x3000
#define VI6_HGO_SIZE 0x3004
#define VI6_HGO_MODE 0x3008
#define VI6_HGO_LB_TH 0x300c
#define VI6_HGO_LBn_H(n) (0x3010 + (n) * 8)
#define VI6_HGO_LBn_V(n) (0x3014 + (n) * 8)
#define VI6_HGO_R_HISTO 0x3030
#define VI6_HGO_R_MAXMIN 0x3130
#define VI6_HGO_R_SUM 0x3134
#define VI6_HGO_R_LB_DET 0x3138
#define VI6_HGO_G_HISTO 0x3140
#define VI6_HGO_G_MAXMIN 0x3240
#define VI6_HGO_G_SUM 0x3244
#define VI6_HGO_G_LB_DET 0x3248
#define VI6_HGO_B_HISTO 0x3250
#define VI6_HGO_B_MAXMIN 0x3350
#define VI6_HGO_B_SUM 0x3354
#define VI6_HGO_B_LB_DET 0x3358
#define VI6_HGO_REGRST 0x33fc
/* -----------------------------------------------------------------------------
* HGT Control Registers
*/
#define VI6_HGT_OFFSET 0x3400
#define VI6_HGT_SIZE 0x3404
#define VI6_HGT_MODE 0x3408
#define VI6_HGT_HUE_AREA(n) (0x340c + (n) * 4)
#define VI6_HGT_LB_TH 0x3424
#define VI6_HGT_LBn_H(n) (0x3438 + (n) * 8)
#define VI6_HGT_LBn_V(n) (0x342c + (n) * 8)
#define VI6_HGT_HISTO(m, n) (0x3450 + (m) * 128 + (n) * 4)
#define VI6_HGT_MAXMIN 0x3750
#define VI6_HGT_SUM 0x3754
#define VI6_HGT_LB_DET 0x3758
#define VI6_HGT_REGRST 0x37fc
/* -----------------------------------------------------------------------------
* LIF Control Registers
*/
#define VI6_LIF_CTRL 0x3b00
#define VI6_LIF_CTRL_OBTH_MASK (0x7ff << 16)
#define VI6_LIF_CTRL_OBTH_SHIFT 16
#define VI6_LIF_CTRL_CFMT (1 << 4)
#define VI6_LIF_CTRL_REQSEL (1 << 1)
#define VI6_LIF_CTRL_LIF_EN (1 << 0)
#define VI6_LIF_CSBTH 0x3b04
#define VI6_LIF_CSBTH_HBTH_MASK (0x7ff << 16)
#define VI6_LIF_CSBTH_HBTH_SHIFT 16
#define VI6_LIF_CSBTH_LBTH_MASK (0x7ff << 0)
#define VI6_LIF_CSBTH_LBTH_SHIFT 0
/* -----------------------------------------------------------------------------
* Security Control Registers
*/
#define VI6_SECURITY_CTRL0 0x3d00
#define VI6_SECURITY_CTRL1 0x3d04
/* -----------------------------------------------------------------------------
* RPF CLUT Registers
*/
#define VI6_CLUT_TABLE 0x4000
/* -----------------------------------------------------------------------------
* 1D LUT Registers
*/
#define VI6_LUT_TABLE 0x7000
/* -----------------------------------------------------------------------------
* 3D LUT Registers
*/
#define VI6_CLU_ADDR 0x7400
#define VI6_CLU_DATA 0x7404
/* -----------------------------------------------------------------------------
* Formats
*/
#define VI6_FMT_RGB_332 0x00
#define VI6_FMT_XRGB_4444 0x01
#define VI6_FMT_RGBX_4444 0x02
#define VI6_FMT_XRGB_1555 0x04
#define VI6_FMT_RGBX_5551 0x05
#define VI6_FMT_RGB_565 0x06
#define VI6_FMT_AXRGB_86666 0x07
#define VI6_FMT_RGBXA_66668 0x08
#define VI6_FMT_XRGBA_66668 0x09
#define VI6_FMT_ARGBX_86666 0x0a
#define VI6_FMT_AXRXGXB_8262626 0x0b
#define VI6_FMT_XRXGXBA_2626268 0x0c
#define VI6_FMT_ARXGXBX_8626262 0x0d
#define VI6_FMT_RXGXBXA_6262628 0x0e
#define VI6_FMT_XRGB_6666 0x0f
#define VI6_FMT_RGBX_6666 0x10
#define VI6_FMT_XRXGXB_262626 0x11
#define VI6_FMT_RXGXBX_626262 0x12
#define VI6_FMT_ARGB_8888 0x13
#define VI6_FMT_RGBA_8888 0x14
#define VI6_FMT_RGB_888 0x15
#define VI6_FMT_XRGXGB_763763 0x16
#define VI6_FMT_XXRGB_86666 0x17
#define VI6_FMT_BGR_888 0x18
#define VI6_FMT_ARGB_4444 0x19
#define VI6_FMT_RGBA_4444 0x1a
#define VI6_FMT_ARGB_1555 0x1b
#define VI6_FMT_RGBA_5551 0x1c
#define VI6_FMT_ABGR_4444 0x1d
#define VI6_FMT_BGRA_4444 0x1e
#define VI6_FMT_ABGR_1555 0x1f
#define VI6_FMT_BGRA_5551 0x20
#define VI6_FMT_XBXGXR_262626 0x21
#define VI6_FMT_ABGR_8888 0x22
#define VI6_FMT_XXRGB_88565 0x23
#define VI6_FMT_Y_UV_444 0x40
#define VI6_FMT_Y_UV_422 0x41
#define VI6_FMT_Y_UV_420 0x42
#define VI6_FMT_YUV_444 0x46
#define VI6_FMT_YUYV_422 0x47
#define VI6_FMT_YYUV_422 0x48
#define VI6_FMT_YUV_420 0x49
#define VI6_FMT_Y_U_V_444 0x4a
#define VI6_FMT_Y_U_V_422 0x4b
#define VI6_FMT_Y_U_V_420 0x4c
#endif /* __VSP1_REGS_H__ */

View file

@ -0,0 +1,209 @@
/*
* vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
*
* Copyright (C) 2013 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.
*/
#include <linux/device.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
#define RPF_MAX_WIDTH 8190
#define RPF_MAX_HEIGHT 8190
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg)
{
return vsp1_read(rpf->entity.vsp1,
reg + rpf->entity.index * VI6_RPF_OFFSET);
}
static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data)
{
vsp1_write(rpf->entity.vsp1,
reg + rpf->entity.index * VI6_RPF_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_rwpf *rpf = to_rwpf(subdev);
const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo;
const struct v4l2_pix_format_mplane *format = &rpf->video.format;
u32 pstride;
u32 infmt;
if (!enable)
return 0;
/* Source size and stride. Cropping isn't supported yet. */
vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE,
(format->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
(format->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE,
(format->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
(format->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
pstride = format->plane_fmt[0].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
if (format->num_planes > 1)
pstride |= format->plane_fmt[1].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride);
/* Format */
infmt = VI6_RPF_INFMT_CIPM
| (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
if (fmtinfo->swap_yc)
infmt |= VI6_RPF_INFMT_SPYCS;
if (fmtinfo->swap_uv)
infmt |= VI6_RPF_INFMT_SPUVS;
if (rpf->entity.formats[RWPF_PAD_SINK].code !=
rpf->entity.formats[RWPF_PAD_SOURCE].code)
infmt |= VI6_RPF_INFMT_CSC;
vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);
vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap);
/* Output location. Composing isn't supported yet. */
vsp1_rpf_write(rpf, VI6_RPF_LOC, 0);
/* Disable alpha, mask and color key. Set the alpha channel to a fixed
* value of 255.
*/
vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED);
vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops rpf_video_ops = {
.s_stream = rpf_s_stream,
};
static struct v4l2_subdev_pad_ops rpf_pad_ops = {
.enum_mbus_code = vsp1_rwpf_enum_mbus_code,
.enum_frame_size = vsp1_rwpf_enum_frame_size,
.get_fmt = vsp1_rwpf_get_format,
.set_fmt = vsp1_rwpf_set_format,
};
static struct v4l2_subdev_ops rpf_ops = {
.video = &rpf_video_ops,
.pad = &rpf_pad_ops,
};
/* -----------------------------------------------------------------------------
* Video Device Operations
*/
static void rpf_vdev_queue(struct vsp1_video *video,
struct vsp1_video_buffer *buf)
{
struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video);
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0]);
if (buf->buf.num_planes > 1)
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, buf->addr[1]);
if (buf->buf.num_planes > 2)
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, buf->addr[2]);
}
static const struct vsp1_video_operations rpf_vdev_ops = {
.queue = rpf_vdev_queue,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
{
struct v4l2_subdev *subdev;
struct vsp1_video *video;
struct vsp1_rwpf *rpf;
int ret;
rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL);
if (rpf == NULL)
return ERR_PTR(-ENOMEM);
rpf->max_width = RPF_MAX_WIDTH;
rpf->max_height = RPF_MAX_HEIGHT;
rpf->entity.type = VSP1_ENTITY_RPF;
rpf->entity.index = index;
rpf->entity.id = VI6_DPR_NODE_RPF(index);
ret = vsp1_entity_init(vsp1, &rpf->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &rpf->entity.subdev;
v4l2_subdev_init(subdev, &rpf_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u",
dev_name(vsp1->dev), index);
v4l2_set_subdevdata(subdev, rpf);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
/* Initialize the video device. */
video = &rpf->video;
video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
video->vsp1 = vsp1;
video->ops = &rpf_vdev_ops;
ret = vsp1_video_init(video, &rpf->entity);
if (ret < 0)
goto error_video;
/* Connect the video device to the RPF. */
ret = media_entity_create_link(&rpf->video.video.entity, 0,
&rpf->entity.subdev.entity,
RWPF_PAD_SINK,
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
if (ret < 0)
goto error_link;
return rpf;
error_link:
vsp1_video_cleanup(video);
error_video:
media_entity_cleanup(&rpf->entity.subdev.entity);
return ERR_PTR(ret);
}

View file

@ -0,0 +1,124 @@
/*
* vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters
*
* Copyright (C) 2013 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.
*/
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
#define RWPF_MIN_WIDTH 1
#define RWPF_MIN_HEIGHT 1
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
V4L2_MBUS_FMT_ARGB8888_1X32,
V4L2_MBUS_FMT_AYUV8_1X32,
};
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
return 0;
}
int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_mbus_framefmt *format;
format = v4l2_subdev_get_try_format(fh, fse->pad);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == RWPF_PAD_SINK) {
fse->min_width = RWPF_MIN_WIDTH;
fse->max_width = rwpf->max_width;
fse->min_height = RWPF_MIN_HEIGHT;
fse->max_height = rwpf->max_height;
} else {
/* The size on the source pad are fixed and always identical to
* the size on the sink pad.
*/
fse->min_width = format->width;
fse->max_width = format->width;
fse->min_height = format->height;
fse->max_height = format->height;
}
return 0;
}
int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
fmt->which);
return 0;
}
int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_mbus_framefmt *format;
/* Default to YUV if the requested format is not supported. */
if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
fmt->which);
if (fmt->pad == RWPF_PAD_SOURCE) {
/* The RWPF performs format conversion but can't scale, only the
* format code can be changed on the source pad.
*/
format->code = fmt->format.code;
fmt->format = *format;
return 0;
}
format->code = fmt->format.code;
format->width = clamp_t(unsigned int, fmt->format.width,
RWPF_MIN_WIDTH, rwpf->max_width);
format->height = clamp_t(unsigned int, fmt->format.height,
RWPF_MIN_HEIGHT, rwpf->max_height);
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(&rwpf->entity, fh, RWPF_PAD_SOURCE,
fmt->which);
*format = fmt->format;
return 0;
}

View file

@ -0,0 +1,53 @@
/*
* vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters
*
* Copyright (C) 2013 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_RWPF_H__
#define __VSP1_RWPF_H__
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_entity.h"
#include "vsp1_video.h"
#define RWPF_PAD_SINK 0
#define RWPF_PAD_SOURCE 1
struct vsp1_rwpf {
struct vsp1_entity entity;
struct vsp1_video video;
unsigned int max_width;
unsigned int max_height;
};
static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_rwpf, entity.subdev);
}
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_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code);
int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse);
int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt);
int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt);
#endif /* __VSP1_RWPF_H__ */

View file

@ -0,0 +1,346 @@
/*
* vsp1_uds.c -- R-Car VSP1 Up and Down Scaler
*
* Copyright (C) 2013 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.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_uds.h"
#define UDS_MIN_SIZE 4U
#define UDS_MAX_SIZE 8190U
#define UDS_MIN_FACTOR 0x0100
#define UDS_MAX_FACTOR 0xffff
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_uds_read(struct vsp1_uds *uds, u32 reg)
{
return vsp1_read(uds->entity.vsp1,
reg + uds->entity.index * VI6_UDS_OFFSET);
}
static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data)
{
vsp1_write(uds->entity.vsp1,
reg + uds->entity.index * VI6_UDS_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* Scaling Computation
*/
/*
* uds_output_size - Return the output size for an input size and scaling ratio
* @input: input size in pixels
* @ratio: scaling ratio in U4.12 fixed-point format
*/
static unsigned int uds_output_size(unsigned int input, unsigned int ratio)
{
if (ratio > 4096) {
/* Down-scaling */
unsigned int mp;
mp = ratio / 4096;
mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);
return (input - 1) / mp * mp * 4096 / ratio + 1;
} else {
/* Up-scaling */
return (input - 1) * 4096 / ratio + 1;
}
}
/*
* uds_output_limits - Return the min and max output sizes for an input size
* @input: input size in pixels
* @minimum: minimum output size (returned)
* @maximum: maximum output size (returned)
*/
static void uds_output_limits(unsigned int input,
unsigned int *minimum, unsigned int *maximum)
{
*minimum = max(uds_output_size(input, UDS_MAX_FACTOR), UDS_MIN_SIZE);
*maximum = min(uds_output_size(input, UDS_MIN_FACTOR), UDS_MAX_SIZE);
}
/*
* uds_passband_width - Return the passband filter width for a scaling ratio
* @ratio: scaling ratio in U4.12 fixed-point format
*/
static unsigned int uds_passband_width(unsigned int ratio)
{
if (ratio >= 4096) {
/* Down-scaling */
unsigned int mp;
mp = ratio / 4096;
mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);
return 64 * 4096 * mp / ratio;
} else {
/* Up-scaling */
return 64;
}
}
static unsigned int uds_compute_ratio(unsigned int input, unsigned int output)
{
/* TODO: This is an approximation that will need to be refined. */
return (input - 1) * 4096 / (output - 1);
}
static void uds_compute_ratios(struct vsp1_uds *uds)
{
struct v4l2_mbus_framefmt *input = &uds->entity.formats[UDS_PAD_SINK];
struct v4l2_mbus_framefmt *output =
&uds->entity.formats[UDS_PAD_SOURCE];
uds->hscale = uds_compute_ratio(input->width, output->width);
uds->vscale = uds_compute_ratio(input->height, output->height);
dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n",
uds->hscale, uds->vscale);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int uds_s_stream(struct v4l2_subdev *subdev, int enable)
{
const struct v4l2_mbus_framefmt *format;
struct vsp1_uds *uds = to_uds(subdev);
if (!enable)
return 0;
/* Enable multi-tap scaling. */
vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_BC);
vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH,
(uds_passband_width(uds->hscale)
<< VI6_UDS_PASS_BWIDTH_H_SHIFT) |
(uds_passband_width(uds->vscale)
<< VI6_UDS_PASS_BWIDTH_V_SHIFT));
/* Set the scaling ratios and the output size. */
format = &uds->entity.formats[UDS_PAD_SOURCE];
vsp1_uds_write(uds, VI6_UDS_SCALE,
(uds->hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
(uds->vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE,
(format->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
(format->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
static int uds_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
V4L2_MBUS_FMT_ARGB8888_1X32,
V4L2_MBUS_FMT_AYUV8_1X32,
};
if (code->pad == UDS_PAD_SINK) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
struct v4l2_mbus_framefmt *format;
/* The UDS can't perform format conversion, the sink format is
* always identical to the source format.
*/
if (code->index)
return -EINVAL;
format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
code->code = format->code;
}
return 0;
}
static int uds_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct v4l2_mbus_framefmt *format;
format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == UDS_PAD_SINK) {
fse->min_width = UDS_MIN_SIZE;
fse->max_width = UDS_MAX_SIZE;
fse->min_height = UDS_MIN_SIZE;
fse->max_height = UDS_MAX_SIZE;
} else {
uds_output_limits(format->width, &fse->min_width,
&fse->max_width);
uds_output_limits(format->height, &fse->min_height,
&fse->max_height);
}
return 0;
}
static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_uds *uds = to_uds(subdev);
fmt->format = *vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
fmt->which);
return 0;
}
static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh,
unsigned int pad, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
struct v4l2_mbus_framefmt *format;
unsigned int minimum;
unsigned int maximum;
switch (pad) {
case UDS_PAD_SINK:
/* Default to YUV if the requested format is not supported. */
if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE);
fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE);
break;
case UDS_PAD_SOURCE:
/* The UDS scales but can't perform format conversion. */
format = vsp1_entity_get_pad_format(&uds->entity, fh,
UDS_PAD_SINK, which);
fmt->code = format->code;
uds_output_limits(format->width, &minimum, &maximum);
fmt->width = clamp(fmt->width, minimum, maximum);
uds_output_limits(format->height, &minimum, &maximum);
fmt->height = clamp(fmt->height, minimum, maximum);
break;
}
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_uds *uds = to_uds(subdev);
struct v4l2_mbus_framefmt *format;
uds_try_format(uds, fh, fmt->pad, &fmt->format, fmt->which);
format = vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
fmt->which);
*format = fmt->format;
if (fmt->pad == UDS_PAD_SINK) {
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&uds->entity, fh,
UDS_PAD_SOURCE, fmt->which);
*format = fmt->format;
uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which);
}
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
uds_compute_ratios(uds);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops uds_video_ops = {
.s_stream = uds_s_stream,
};
static struct v4l2_subdev_pad_ops uds_pad_ops = {
.enum_mbus_code = uds_enum_mbus_code,
.enum_frame_size = uds_enum_frame_size,
.get_fmt = uds_get_format,
.set_fmt = uds_set_format,
};
static struct v4l2_subdev_ops uds_ops = {
.video = &uds_video_ops,
.pad = &uds_pad_ops,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)
{
struct v4l2_subdev *subdev;
struct vsp1_uds *uds;
int ret;
uds = devm_kzalloc(vsp1->dev, sizeof(*uds), GFP_KERNEL);
if (uds == NULL)
return ERR_PTR(-ENOMEM);
uds->entity.type = VSP1_ENTITY_UDS;
uds->entity.index = index;
uds->entity.id = VI6_DPR_NODE_UDS(index);
ret = vsp1_entity_init(vsp1, &uds->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &uds->entity.subdev;
v4l2_subdev_init(subdev, &uds_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s uds.%u",
dev_name(vsp1->dev), index);
v4l2_set_subdevdata(subdev, uds);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
return uds;
}

View file

@ -0,0 +1,40 @@
/*
* vsp1_uds.h -- R-Car VSP1 Up and Down Scaler
*
* Copyright (C) 2013 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_UDS_H__
#define __VSP1_UDS_H__
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
struct vsp1_device;
#define UDS_PAD_SINK 0
#define UDS_PAD_SOURCE 1
struct vsp1_uds {
struct vsp1_entity entity;
unsigned int hscale;
unsigned int vscale;
};
static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_uds, entity.subdev);
}
struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index);
#endif /* __VSP1_UDS_H__ */

File diff suppressed because it is too large Load diff

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