media updates for v4.7-rc1

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJXPDJgAAoJEAhfPr2O5OEVtqIP/0Sz8w0RkfvBO1Rw39C5fuhm
 Qc5ANFwVEAVMq+0nSGfBUzRX1a6P7g7sJrWFeXzJ4iY3wInUK1CJKxUnfz/gvuNt
 Xjplq6ebvh81SP3VGaXJwBesH56NHfUCOioQnk8Oyo9MBsXwgBKvAbby2YiqwuP/
 1t6meQ/pSSqdC4g6Z5hrRIzlLEvf14DKOgDOBUHF1wdn8So3mXSDFwW3Y8T4C4g6
 1WDqmCG8qlIMo4xMj4Dqk6bqgRkWnHuUrENkSWlvzguXkspKyRp/TEkOrJxqJnyp
 ZlFoIW7KSLl7uPY1e/JM6BI8v74WQLfmph37a7hEmsAVmCbVTskiKb+rfbDj6vOG
 THBKJhocsPrAtBjnK0C5Xgb6e64dHw84okVRb/6uz67qqdg5c3sRerf9pZR8QkCS
 P6ng27ulbfJAf4qQdEeP8cmWUQrif4tNjCv5qPTL9hEATNKnJAF7rAaR8Zv+Qf/G
 wTQ3fJ9qofS9xaKfNjKBtbVRNbtYoDysNJlvhEp6SWfVR+HyDoWiH9HMvryyG/Gk
 VAlQfhGRsocNvDW8V5j94NiKI3LjVtJMS3QOqbYyaChTSFSaJ5fRWcqQskweDRgS
 glS80VhlHzoHx3C81VxE7V7kNtGdxiIzgnQUaVtEV4w5FcbxEJG3ElUdaFVVjDgZ
 xVoie3zxLM2E9Q5hhIE+
 =s0vv
 -----END PGP SIGNATURE-----

Merge tag 'media/v4.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull media updates from Mauro Carvalho Chehab:
 - added support for Intersil/Techwell TW686x-based video capture cards
 - v4l PCI skeleton driver moved to samples directory
 - Documentation cleanups and improvements
 - RC: reduced the memory footprint for IR raw events
 - tpg: Export the tpg code from vivid as a module
 - adv7180: Add device tree binding documentation
 - lots of driver improvements and fixes

* tag 'media/v4.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (173 commits)
  [media] exynos-gsc: avoid build warning without CONFIG_OF
  [media] samples: v4l: from Documentation to samples directory
  [media] dib0700: add USB ID for another STK8096-PVR ref design based card
  [media] tvp5150: propagate I2C write error in .s_register callback
  [media] tvp5150: return I2C write operation failure to callers
  [media] em28xx: add support for Hauppauge WinTV-dualHD DVB tuner
  [media] em28xx: add missing USB IDs
  [media] update cx23885 and em28xx cardlists
  [media] media: au0828 fix au0828_v4l2_device_register() to not unlock and free
  [media] c8sectpfe: Rework firmware loading mechanism
  [media] c8sectpfe: Demote print to dev_dbg
  [media] c8sectpfe: Fix broken circular buffer wp management
  [media] media-device: Simplify compat32 logic
  [media] media: i2c: ths7303: remove redundant assignment on bt
  [media] dvb-usb: hide unused functions
  [media] xilinx-vipp: remove unnecessary of_node_put
  [media] drivers/media/media-devnode: clear private_data before put_device()
  [media] drivers/media/media-device: move debug log before _devnode_unregister()
  [media] drivers/media/rc: postpone kfree(rc_dev)
  [media] media/dvb-core: forward media_create_pad_links() return value
  ...
This commit is contained in:
Linus Torvalds 2016-05-18 17:03:51 -07:00
commit 19c5abcb74
192 changed files with 6757 additions and 3001 deletions

View file

@ -19,3 +19,4 @@ config CYPRESS_FIRMWARE
source "drivers/media/common/b2c2/Kconfig"
source "drivers/media/common/saa7146/Kconfig"
source "drivers/media/common/siano/Kconfig"
source "drivers/media/common/v4l2-tpg/Kconfig"

View file

@ -1,4 +1,4 @@
obj-y += b2c2/ saa7146/ siano/
obj-y += b2c2/ saa7146/ siano/ v4l2-tpg/
obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o

View file

@ -0,0 +1,2 @@
config VIDEO_V4L2_TPG
tristate

View file

@ -0,0 +1,3 @@
v4l2-tpg-objs := v4l2-tpg-core.o v4l2-tpg-colors.o
obj-$(CONFIG_VIDEO_V4L2_TPG) += v4l2-tpg.o

View file

@ -1,5 +1,5 @@
/*
* vivid-color.c - A table that converts colors to various colorspaces
* v4l2-tpg-colors.c - A table that converts colors to various colorspaces
*
* The test pattern generator uses the tpg_colors for its test patterns.
* For testing colorspaces the first 8 colors of that table need to be
@ -12,7 +12,7 @@
* This source also contains the code used to generate the tpg_csc_colors
* table. Run the following command to compile it:
*
* gcc vivid-tpg-colors.c -DCOMPILE_APP -o gen-colors -lm
* gcc v4l2-tpg-colors.c -DCOMPILE_APP -o gen-colors -lm
*
* and run the utility.
*
@ -36,8 +36,7 @@
*/
#include <linux/videodev2.h>
#include "vivid-tpg-colors.h"
#include <media/v4l2-tpg-colors.h>
/* sRGB colors with range [0-255] */
const struct color tpg_colors[TPG_COLOR_MAX] = {

View file

@ -1,5 +1,5 @@
/*
* vivid-tpg.c - Test Pattern Generator
* v4l2-tpg-core.c - Test Pattern Generator
*
* Note: gen_twopix and tpg_gen_text are based on code from vivi.c. See the
* vivi.c source for the copyright information of those functions.
@ -20,7 +20,8 @@
* SOFTWARE.
*/
#include "vivid-tpg.h"
#include <linux/module.h>
#include <media/v4l2-tpg.h>
/* Must remain in sync with enum tpg_pattern */
const char * const tpg_pattern_strings[] = {
@ -48,6 +49,7 @@ const char * const tpg_pattern_strings[] = {
"Noise",
NULL
};
EXPORT_SYMBOL_GPL(tpg_pattern_strings);
/* Must remain in sync with enum tpg_aspect */
const char * const tpg_aspect_strings[] = {
@ -58,6 +60,7 @@ const char * const tpg_aspect_strings[] = {
"16x9 Anamorphic",
NULL
};
EXPORT_SYMBOL_GPL(tpg_aspect_strings);
/*
* Sine table: sin[0] = 127 * sin(-180 degrees)
@ -93,6 +96,7 @@ void tpg_set_font(const u8 *f)
{
font8x16 = f;
}
EXPORT_SYMBOL_GPL(tpg_set_font);
void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h)
{
@ -114,6 +118,7 @@ void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h)
tpg->colorspace = V4L2_COLORSPACE_SRGB;
tpg->perc_fill = 100;
}
EXPORT_SYMBOL_GPL(tpg_init);
int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
{
@ -150,6 +155,7 @@ int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
}
return 0;
}
EXPORT_SYMBOL_GPL(tpg_alloc);
void tpg_free(struct tpg_data *tpg)
{
@ -174,6 +180,7 @@ void tpg_free(struct tpg_data *tpg)
tpg->random_line[plane] = NULL;
}
}
EXPORT_SYMBOL_GPL(tpg_free);
bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
{
@ -403,6 +410,7 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
}
return true;
}
EXPORT_SYMBOL_GPL(tpg_s_fourcc);
void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
const struct v4l2_rect *compose)
@ -418,6 +426,7 @@ void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
tpg->scaled_width = 2;
tpg->recalc_lines = true;
}
EXPORT_SYMBOL_GPL(tpg_s_crop_compose);
void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
u32 field)
@ -442,6 +451,7 @@ void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
(2 * tpg->hdownsampling[p]);
tpg->recalc_square_border = true;
}
EXPORT_SYMBOL_GPL(tpg_reset_source);
static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg)
{
@ -1250,6 +1260,7 @@ unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line)
return 0;
}
}
EXPORT_SYMBOL_GPL(tpg_g_interleaved_plane);
/* Return how many pattern lines are used by the current pattern. */
static unsigned tpg_get_pat_lines(const struct tpg_data *tpg)
@ -1725,6 +1736,7 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
}
}
}
EXPORT_SYMBOL_GPL(tpg_gen_text);
void tpg_update_mv_step(struct tpg_data *tpg)
{
@ -1773,6 +1785,7 @@ void tpg_update_mv_step(struct tpg_data *tpg)
if (factor < 0)
tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step;
}
EXPORT_SYMBOL_GPL(tpg_update_mv_step);
/* Map the line number relative to the crop rectangle to a frame line number */
static unsigned tpg_calc_frameline(const struct tpg_data *tpg, unsigned src_y,
@ -1862,6 +1875,7 @@ void tpg_calc_text_basep(struct tpg_data *tpg,
if (p == 0 && tpg->interleaved)
tpg_calc_text_basep(tpg, basep, 1, vbuf);
}
EXPORT_SYMBOL_GPL(tpg_calc_text_basep);
static int tpg_pattern_avg(const struct tpg_data *tpg,
unsigned pat1, unsigned pat2)
@ -1891,6 +1905,7 @@ void tpg_log_status(struct tpg_data *tpg)
pr_info("tpg quantization: %d/%d\n", tpg->quantization, tpg->real_quantization);
pr_info("tpg RGB range: %d/%d\n", tpg->rgb_range, tpg->real_rgb_range);
}
EXPORT_SYMBOL_GPL(tpg_log_status);
/*
* This struct contains common parameters used by both the drawing of the
@ -2296,6 +2311,7 @@ void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std,
vbuf + buf_line * params.stride);
}
}
EXPORT_SYMBOL_GPL(tpg_fill_plane_buffer);
void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
{
@ -2312,3 +2328,8 @@ void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
offset += tpg_calc_plane_size(tpg, i);
}
}
EXPORT_SYMBOL_GPL(tpg_fillbuffer);
MODULE_DESCRIPTION("V4L2 Test Pattern Generator");
MODULE_AUTHOR("Hans Verkuil");
MODULE_LICENSE("GPL");

View file

@ -58,6 +58,14 @@
#define USB_VID_TELESTAR 0x10b9
#define USB_VID_VISIONPLUS 0x13d3
#define USB_VID_SONY 0x1415
#define USB_PID_TEVII_S421 0xd421
#define USB_PID_TEVII_S480_1 0xd481
#define USB_PID_TEVII_S480_2 0xd482
#define USB_PID_TEVII_S630 0xd630
#define USB_PID_TEVII_S632 0xd632
#define USB_PID_TEVII_S650 0xd650
#define USB_PID_TEVII_S660 0xd660
#define USB_PID_TEVII_S662 0xd662
#define USB_VID_TWINHAN 0x1822
#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
#define USB_VID_UNIWILL 0x1584
@ -141,6 +149,7 @@
#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
#define USB_PID_GOTVIEW_SAT_HD 0x5456
#define USB_PID_INTEL_CE9500 0x9500
#define USB_PID_ITETECH_IT9135 0x9135
#define USB_PID_ITETECH_IT9135_9005 0x9005
@ -159,6 +168,8 @@
#define USB_PID_KWORLD_UB499_2T_T09 0xe409
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#define USB_PID_PROF_1100 0xb012
#define USB_PID_TERRATEC_CINERGY_S 0x0064
#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069
#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093
@ -361,6 +372,8 @@
#define USB_PID_YUAN_STK7700D 0x1efc
#define USB_PID_YUAN_STK7700D_2 0x1e8c
#define USB_PID_DW2102 0x2102
#define USB_PID_DW2104 0x2104
#define USB_PID_DW3101 0x3101
#define USB_PID_XTENSIONS_XD_380 0x0381
#define USB_PID_TELESTAR_STARSTICK_2 0x8000
#define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807
@ -373,6 +386,7 @@
#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020
#define USB_PID_ELGATO_EYETV_SAT 0x002a
#define USB_PID_ELGATO_EYETV_SAT_V2 0x0025
#define USB_PID_ELGATO_EYETV_SAT_V3 0x0036
#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000
#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001
#define USB_PID_FRIIO_WHITE 0x0001

View file

@ -676,13 +676,13 @@ int dvb_create_media_graph(struct dvb_adapter *adap,
demux, 0, MEDIA_LNK_FL_ENABLED,
false);
if (ret)
return -ENOMEM;
return ret;
}
if (demux && ca) {
ret = media_create_pad_link(demux, 1, ca,
0, MEDIA_LNK_FL_ENABLED);
if (ret)
return -ENOMEM;
return ret;
}
/* Create demux links for each ringbuffer/pad */

View file

@ -1121,7 +1121,7 @@ void dib0090_pwm_gain_reset(struct dvb_frontend *fe)
(state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND",
state->identity.version & 0x1f);
if (rf_ramp && ((state->rf_ramp[0] == 0) ||
if (rf_ramp && ((state->rf_ramp && state->rf_ramp[0] == 0) ||
(state->current_band == BAND_CBAND &&
(state->identity.version & 0x1f) <= P1D_E_F))) {
dprintk("DE-Engage mux for direct gain reg control");

View file

@ -458,7 +458,7 @@ static int ds3000_read_status(struct dvb_frontend *fe, enum fe_status *status)
break;
default:
return 1;
return -EINVAL;
}
if (state->config->set_lock_led)
@ -528,7 +528,7 @@ static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber)
*ber = 0xffffffff;
break;
default:
return 1;
return -EINVAL;
}
return 0;
@ -623,7 +623,7 @@ static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr)
snr_reading, *snr);
break;
default:
return 1;
return -EINVAL;
}
return 0;
@ -661,7 +661,7 @@ static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
state->prevUCBS2 = _ucblocks;
break;
default:
return 1;
return -EINVAL;
}
return 0;
@ -754,7 +754,7 @@ static int ds3000_send_diseqc_msg(struct dvb_frontend *fe,
data |= 0x80;
ds3000_writereg(state, 0xa2, data);
return 1;
return -ETIMEDOUT;
}
data = ds3000_readreg(state, 0xa2);
@ -808,7 +808,7 @@ static int ds3000_diseqc_send_burst(struct dvb_frontend *fe,
data |= 0x80;
ds3000_writereg(state, 0xa2, data);
return 1;
return -ETIMEDOUT;
}
data = ds3000_readreg(state, 0xa2);
@ -951,7 +951,7 @@ static int ds3000_set_frontend(struct dvb_frontend *fe)
ds3000_writereg(state, 0xfe, 0x98);
break;
default:
return 1;
return -EINVAL;
}
/* enable 27MHz clock output */

View file

@ -46,7 +46,7 @@ struct m88ds3103_dev {
/* auto detect chip id to do different config */
u8 chip_id;
/* main mclk is calculated for M88RS6000 dynamically */
u32 mclk_khz;
s32 mclk_khz;
u64 post_bit_error;
u64 post_bit_count;
};

View file

@ -135,8 +135,7 @@ static void zl10353_calc_nominal_rate(struct dvb_frontend *fe,
value = (u64)10 * (1 << 23) / 7 * 125;
value = (bw * value) + adc_clock / 2;
do_div(value, adc_clock);
*nominal_rate = value;
*nominal_rate = div_u64(value, adc_clock);
dprintk("%s: bw %d, adc_clock %d => 0x%x\n",
__func__, bw, adc_clock, *nominal_rate);
@ -163,8 +162,7 @@ static void zl10353_calc_input_freq(struct dvb_frontend *fe,
if (ife > adc_clock / 2)
ife = adc_clock - ife;
}
value = (u64)65536 * ife + adc_clock / 2;
do_div(value, adc_clock);
value = div_u64((u64)65536 * ife + adc_clock / 2, adc_clock);
*input_freq = -value;
dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n",

View file

@ -1130,8 +1130,6 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *
hdl = &state->hdl;
v4l2_ctrl_handler_init(hdl, 5);
/* private controls */
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);
@ -1151,12 +1149,6 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *
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_pads_init(&sd->entity, 1, &state->pad);
if (err)

View file

@ -466,9 +466,9 @@ static int adp1653_of_init(struct i2c_client *client,
of_node_put(child);
pd->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_LOW);
if (!pd->enable_gpio) {
if (IS_ERR(pd->enable_gpio)) {
dev_err(&client->dev, "Error getting GPIO\n");
return -EINVAL;
return PTR_ERR(pd->enable_gpio);
}
return 0;

View file

@ -26,8 +26,9 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <media/v4l2-ioctl.h>
#include <linux/videodev2.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <linux/mutex.h>
@ -192,8 +193,8 @@ struct adv7180_state {
struct mutex mutex; /* mutual excl. when accessing chip */
int irq;
v4l2_std_id curr_norm;
bool autodetect;
bool powered;
bool streaming;
u8 input;
struct i2c_client *client;
@ -338,12 +339,26 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
if (err)
return err;
/* when we are interrupt driven we know the state */
if (!state->autodetect || state->irq > 0)
*std = state->curr_norm;
else
err = __adv7180_status(state, NULL, std);
if (state->streaming) {
err = -EBUSY;
goto unlock;
}
err = adv7180_set_video_standard(state,
ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
if (err)
goto unlock;
msleep(100);
__adv7180_status(state, NULL, std);
err = v4l2_std_to_adv7180(state->curr_norm);
if (err < 0)
goto unlock;
err = adv7180_set_video_standard(state, err);
unlock:
mutex_unlock(&state->mutex);
return err;
}
@ -387,23 +402,13 @@ static int adv7180_program_std(struct adv7180_state *state)
{
int ret;
if (state->autodetect) {
ret = adv7180_set_video_standard(state,
ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
if (ret < 0)
return ret;
__adv7180_status(state, NULL, &state->curr_norm);
} else {
ret = v4l2_std_to_adv7180(state->curr_norm);
if (ret < 0)
return ret;
ret = adv7180_set_video_standard(state, ret);
if (ret < 0)
return ret;
}
ret = v4l2_std_to_adv7180(state->curr_norm);
if (ret < 0)
return ret;
ret = adv7180_set_video_standard(state, ret);
if (ret < 0)
return ret;
return 0;
}
@ -415,18 +420,12 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
if (ret)
return ret;
/* all standards -> autodetect */
if (std == V4L2_STD_ALL) {
state->autodetect = true;
} else {
/* Make sure we can support this std */
ret = v4l2_std_to_adv7180(std);
if (ret < 0)
goto out;
/* Make sure we can support this std */
ret = v4l2_std_to_adv7180(std);
if (ret < 0)
goto out;
state->curr_norm = std;
state->autodetect = false;
}
state->curr_norm = std;
ret = adv7180_program_std(state);
out:
@ -434,6 +433,15 @@ out:
return ret;
}
static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
{
struct adv7180_state *state = to_state(sd);
*norm = state->curr_norm;
return 0;
}
static int adv7180_set_power(struct adv7180_state *state, bool on)
{
u8 val;
@ -717,17 +725,77 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
return 0;
}
static int adv7180_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *cropcap)
{
struct adv7180_state *state = to_state(sd);
if (state->curr_norm & V4L2_STD_525_60) {
cropcap->pixelaspect.numerator = 11;
cropcap->pixelaspect.denominator = 10;
} else {
cropcap->pixelaspect.numerator = 54;
cropcap->pixelaspect.denominator = 59;
}
return 0;
}
static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
{
*norm = V4L2_STD_ALL;
return 0;
}
static int adv7180_s_stream(struct v4l2_subdev *sd, int enable)
{
struct adv7180_state *state = to_state(sd);
int ret;
/* It's always safe to stop streaming, no need to take the lock */
if (!enable) {
state->streaming = enable;
return 0;
}
/* Must wait until querystd released the lock */
ret = mutex_lock_interruptible(&state->mutex);
if (ret)
return ret;
state->streaming = enable;
mutex_unlock(&state->mutex);
return 0;
}
static int adv7180_subscribe_event(struct v4l2_subdev *sd,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
switch (sub->type) {
case V4L2_EVENT_SOURCE_CHANGE:
return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
case V4L2_EVENT_CTRL:
return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
default:
return -EINVAL;
}
}
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.s_std = adv7180_s_std,
.g_std = adv7180_g_std,
.querystd = adv7180_querystd,
.g_input_status = adv7180_g_input_status,
.s_routing = adv7180_s_routing,
.g_mbus_config = adv7180_g_mbus_config,
.cropcap = adv7180_cropcap,
.g_tvnorms = adv7180_g_tvnorms,
.s_stream = adv7180_s_stream,
};
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.s_power = adv7180_s_power,
.subscribe_event = adv7180_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
@ -752,8 +820,14 @@ static irqreturn_t adv7180_irq(int irq, void *devid)
/* clear */
adv7180_write(state, ADV7180_REG_ICR3, isr3);
if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
__adv7180_status(state, NULL, &state->curr_norm);
if (isr3 & ADV7180_IRQ3_AD_CHANGE) {
static const struct v4l2_event src_ch = {
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
};
v4l2_subdev_notify_event(&state->sd, &src_ch);
}
mutex_unlock(&state->mutex);
return IRQ_HANDLED;
@ -1198,7 +1272,7 @@ static int adv7180_probe(struct i2c_client *client,
state->irq = client->irq;
mutex_init(&state->mutex);
state->autodetect = true;
state->curr_norm = V4L2_STD_NTSC;
if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
state->powered = true;
else
@ -1206,7 +1280,7 @@ static int adv7180_probe(struct i2c_client *client,
state->input = 0;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
ret = adv7180_init_controls(state);
if (ret)
@ -1328,6 +1402,14 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
#ifdef CONFIG_OF
static const struct of_device_id adv7180_of_id[] = {
{ .compatible = "adi,adv7180", },
{ .compatible = "adi,adv7182", },
{ .compatible = "adi,adv7280", },
{ .compatible = "adi,adv7280-m", },
{ .compatible = "adi,adv7281", },
{ .compatible = "adi,adv7281-m", },
{ .compatible = "adi,adv7281-ma", },
{ .compatible = "adi,adv7282", },
{ .compatible = "adi,adv7282-m", },
{ },
};

View file

@ -1502,12 +1502,6 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
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_pads_init(&sd->entity, 1, &state->pad);
if (err)

View file

@ -3141,7 +3141,6 @@ static int adv76xx_probe(struct i2c_client *client,
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
/* private controls */
state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
V4L2_CID_DV_RX_POWER_PRESENT, 0,
(1 << state->info->num_dv_ports) - 1, 0, 0);
@ -3164,13 +3163,6 @@ static int adv76xx_probe(struct i2c_client *client,
err = hdl->error;
goto err_hdl;
}
state->detect_tx_5v_ctrl->is_private = true;
state->rgb_quantization_range_ctrl->is_private = true;
if (adv76xx_has_afe(state))
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 (adv76xx_s_detect_tx_5v_ctrl(sd)) {
err = -ENODEV;
goto err_hdl;

View file

@ -3300,12 +3300,6 @@ static int adv7842_probe(struct i2c_client *client,
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_ctrl_manual->is_private = true;
state->free_run_color_ctrl->is_private = true;
if (adv7842_s_detect_tx_5v_ctrl(sd)) {
err = -ENODEV;
goto err_hdl;

View file

@ -405,7 +405,7 @@ static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
struct v4l2_subdev *sd = to_sd(ctrl);
struct m5mols_info *info = to_m5mols(sd);
int ret = 0;
u8 status;
u8 status = REG_ISO_AUTO;
v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n",
__func__, ctrl->name, info->isp_ready);

View file

@ -1798,6 +1798,21 @@ static int saa711x_detect_chip(struct i2c_client *client,
return GM7113C;
}
/* Check if it is a CJC7113 */
if (!memcmp(name, "1111111111111111", CHIP_VER_SIZE)) {
strlcpy(name, "cjc7113", CHIP_VER_SIZE);
if (!autodetect && strcmp(name, id->name))
return -EINVAL;
v4l_dbg(1, debug, client,
"It seems to be a %s chip (%*ph) @ 0x%x.\n",
name, 16, chip_ver, client->addr << 1);
/* CJC7113 seems to be SAA7113-compatible */
return SAA7113;
}
/* Chip was not discovered. Return its ID and don't bind */
v4l_dbg(1, debug, client, "chip %*ph @ 0x%x is unknown.\n",
16, chip_ver, client->addr << 1);

View file

@ -188,6 +188,8 @@ static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor)
embedded_end = 0;
}
sensor->image_start = image_start;
dev_dbg(&client->dev, "embedded data from lines %d to %d\n",
embedded_start, embedded_end);
dev_dbg(&client->dev, "image data starts at line %d\n", image_start);
@ -2280,6 +2282,15 @@ static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames)
return 0;
}
static int smiapp_get_skip_top_lines(struct v4l2_subdev *subdev, u32 *lines)
{
struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
*lines = sensor->image_start;
return 0;
}
/* -----------------------------------------------------------------------------
* sysfs attributes
*/
@ -2890,6 +2901,7 @@ static const struct v4l2_subdev_pad_ops smiapp_pad_ops = {
static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = {
.g_skip_frames = smiapp_get_skip_frames,
.g_skip_top_lines = smiapp_get_skip_top_lines,
};
static const struct v4l2_subdev_ops smiapp_ops = {

View file

@ -217,6 +217,7 @@ struct smiapp_sensor {
u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */
u8 frame_skip;
u16 image_start; /* Offset to first line after metadata lines */
int power_count;

View file

@ -1551,6 +1551,8 @@ static int tc358743_g_edid(struct v4l2_subdev *sd,
{
struct tc358743_state *state = to_state(sd);
memset(edid->reserved, 0, sizeof(edid->reserved));
if (edid->pad != 0)
return -EINVAL;
@ -1585,6 +1587,8 @@ static int tc358743_s_edid(struct v4l2_subdev *sd,
v4l2_dbg(2, debug, sd, "%s, pad %d, start block %d, blocks %d\n",
__func__, edid->pad, edid->start_block, edid->blocks);
memset(edid->reserved, 0, sizeof(edid->reserved));
if (edid->pad != 0)
return -EINVAL;
@ -1859,7 +1863,6 @@ static int tc358743_probe(struct i2c_client *client,
/* control handlers */
v4l2_ctrl_handler_init(&state->hdl, 3);
/* private controls */
state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&state->hdl, NULL,
V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0);

View file

@ -285,7 +285,7 @@ static int ths7303_log_status(struct v4l2_subdev *sd)
v4l2_info(sd, "stream %s\n", state->stream_on ? "On" : "Off");
if (state->bt.pixelclock) {
struct v4l2_bt_timings *bt = bt = &state->bt;
struct v4l2_bt_timings *bt = &state->bt;
u32 frame_width, frame_height;
frame_width = V4L2_DV_BT_FRAME_WIDTH(bt);

View file

@ -83,7 +83,7 @@ static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
return rc;
}
static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
static int tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
unsigned char value)
{
struct i2c_client *c = v4l2_get_subdevdata(sd);
@ -92,7 +92,9 @@ static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", addr, value);
rc = i2c_smbus_write_byte_data(c, addr, value);
if (rc < 0)
v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d\n", rc);
v4l2_err(sd, "i2c i/o error: rc == %d\n", rc);
return rc;
}
static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
@ -1159,8 +1161,7 @@ static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *
static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
return tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
}
#endif

View file

@ -90,18 +90,13 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id)
id &= ~MEDIA_ENT_ID_FLAG_NEXT;
spin_lock(&mdev->lock);
media_device_for_each_entity(entity, mdev) {
if (((media_entity_id(entity) == id) && !next) ||
((media_entity_id(entity) > id) && next)) {
spin_unlock(&mdev->lock);
return entity;
}
}
spin_unlock(&mdev->lock);
return NULL;
}
@ -431,6 +426,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
struct media_device *dev = to_media_device(devnode);
long ret;
mutex_lock(&dev->graph_mutex);
switch (cmd) {
case MEDIA_IOC_DEVICE_INFO:
ret = media_device_get_info(dev,
@ -443,29 +439,24 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
break;
case MEDIA_IOC_ENUM_LINKS:
mutex_lock(&dev->graph_mutex);
ret = media_device_enum_links(dev,
(struct media_links_enum __user *)arg);
mutex_unlock(&dev->graph_mutex);
break;
case MEDIA_IOC_SETUP_LINK:
mutex_lock(&dev->graph_mutex);
ret = media_device_setup_link(dev,
(struct media_link_desc __user *)arg);
mutex_unlock(&dev->graph_mutex);
break;
case MEDIA_IOC_G_TOPOLOGY:
mutex_lock(&dev->graph_mutex);
ret = media_device_get_topology(dev,
(struct media_v2_topology __user *)arg);
mutex_unlock(&dev->graph_mutex);
break;
default:
ret = -ENOIOCTLCMD;
}
mutex_unlock(&dev->graph_mutex);
return ret;
}
@ -508,12 +499,6 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
long ret;
switch (cmd) {
case MEDIA_IOC_DEVICE_INFO:
case MEDIA_IOC_ENUM_ENTITIES:
case MEDIA_IOC_SETUP_LINK:
case MEDIA_IOC_G_TOPOLOGY:
return media_device_ioctl(filp, cmd, arg);
case MEDIA_IOC_ENUM_LINKS32:
mutex_lock(&dev->graph_mutex);
ret = media_device_enum_links32(dev,
@ -522,7 +507,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
break;
default:
ret = -ENOIOCTLCMD;
return media_device_ioctl(filp, cmd, arg);
}
return ret;
@ -590,12 +575,12 @@ int __must_check media_device_register_entity(struct media_device *mdev,
if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL))
return -ENOMEM;
spin_lock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
ret = ida_get_new_above(&mdev->entity_internal_idx, 1,
&entity->internal_idx);
if (ret < 0) {
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
return ret;
}
@ -615,9 +600,6 @@ int __must_check media_device_register_entity(struct media_device *mdev,
(notify)->notify(entity, notify->notify_data);
}
spin_unlock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
if (mdev->entity_internal_idx_max
>= mdev->pm_count_walk.ent_enum.idx_max) {
struct media_entity_graph new = { .top = 0 };
@ -680,9 +662,9 @@ void media_device_unregister_entity(struct media_entity *entity)
if (mdev == NULL)
return;
spin_lock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
__media_device_unregister_entity(entity);
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
}
EXPORT_SYMBOL_GPL(media_device_unregister_entity);
@ -703,7 +685,6 @@ void media_device_init(struct media_device *mdev)
INIT_LIST_HEAD(&mdev->pads);
INIT_LIST_HEAD(&mdev->links);
INIT_LIST_HEAD(&mdev->entity_notify);
spin_lock_init(&mdev->lock);
mutex_init(&mdev->graph_mutex);
ida_init(&mdev->entity_internal_idx);
@ -752,9 +733,9 @@ EXPORT_SYMBOL_GPL(__media_device_register);
int __must_check media_device_register_entity_notify(struct media_device *mdev,
struct media_entity_notify *nptr)
{
spin_lock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
list_add_tail(&nptr->list, &mdev->entity_notify);
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(media_device_register_entity_notify);
@ -771,9 +752,9 @@ static void __media_device_unregister_entity_notify(struct media_device *mdev,
void media_device_unregister_entity_notify(struct media_device *mdev,
struct media_entity_notify *nptr)
{
spin_lock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
__media_device_unregister_entity_notify(mdev, nptr);
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
}
EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
@ -787,11 +768,11 @@ void media_device_unregister(struct media_device *mdev)
if (mdev == NULL)
return;
spin_lock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
/* Check if mdev was ever registered at all */
if (!media_devnode_is_registered(&mdev->devnode)) {
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
return;
}
@ -811,12 +792,11 @@ void media_device_unregister(struct media_device *mdev)
kfree(intf);
}
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
device_remove_file(&mdev->devnode.dev, &dev_attr_model);
dev_dbg(mdev->dev, "Media device unregistering\n");
media_devnode_unregister(&mdev->devnode);
dev_dbg(mdev->dev, "Media device unregistered\n");
}
EXPORT_SYMBOL_GPL(media_device_unregister);

View file

@ -197,10 +197,11 @@ static int media_release(struct inode *inode, struct file *filp)
if (mdev->fops->release)
mdev->fops->release(filp);
filp->private_data = NULL;
/* decrease the refcount unconditionally since the release()
return value is ignored. */
put_device(&mdev->dev);
filp->private_data = NULL;
return 0;
}
@ -267,8 +268,11 @@ int __must_check media_devnode_register(struct media_devnode *mdev,
return 0;
error:
mutex_lock(&media_devnode_lock);
cdev_del(&mdev->cdev);
clear_bit(mdev->minor, media_devnode_nums);
mutex_unlock(&media_devnode_lock);
return ret;
}

View file

@ -219,7 +219,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
entity->pads = pads;
if (mdev)
spin_lock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
for (i = 0; i < num_pads; i++) {
pads[i].entity = entity;
@ -230,7 +230,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
}
if (mdev)
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
return 0;
}
@ -445,7 +445,7 @@ __must_check int __media_entity_pipeline_start(struct media_entity *entity,
bitmap_or(active, active, has_no_links, entity->num_pads);
if (!bitmap_full(active, entity->num_pads)) {
ret = -EPIPE;
ret = -ENOLINK;
dev_dbg(entity->graph_obj.mdev->dev,
"\"%s\":%u must be connected by an enabled link\n",
entity->name,
@ -747,9 +747,9 @@ void media_entity_remove_links(struct media_entity *entity)
if (mdev == NULL)
return;
spin_lock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
__media_entity_remove_links(entity);
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
}
EXPORT_SYMBOL_GPL(media_entity_remove_links);
@ -951,9 +951,9 @@ void media_remove_intf_link(struct media_link *link)
if (mdev == NULL)
return;
spin_lock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
__media_remove_intf_link(link);
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
}
EXPORT_SYMBOL_GPL(media_remove_intf_link);
@ -975,8 +975,8 @@ void media_remove_intf_links(struct media_interface *intf)
if (mdev == NULL)
return;
spin_lock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
__media_remove_intf_links(intf);
spin_unlock(&mdev->lock);
mutex_unlock(&mdev->graph_mutex);
}
EXPORT_SYMBOL_GPL(media_remove_intf_links);

View file

@ -14,6 +14,7 @@ source "drivers/media/pci/meye/Kconfig"
source "drivers/media/pci/solo6x10/Kconfig"
source "drivers/media/pci/sta2x11/Kconfig"
source "drivers/media/pci/tw68/Kconfig"
source "drivers/media/pci/tw686x/Kconfig"
source "drivers/media/pci/zoran/Kconfig"
endif

View file

@ -25,6 +25,7 @@ obj-$(CONFIG_VIDEO_BT848) += bt8xx/
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
obj-$(CONFIG_VIDEO_TW68) += tw68/
obj-$(CONFIG_VIDEO_TW686X) += tw686x/
obj-$(CONFIG_VIDEO_DT3155) += dt3155/
obj-$(CONFIG_VIDEO_MEYE) += meye/
obj-$(CONFIG_STA2X11_VIP) += sta2x11/

View file

@ -4,6 +4,7 @@ config VIDEO_COBALT
depends on PCI_MSI && MTD_COMPLEX_MAPPINGS
depends on GPIOLIB || COMPILE_TEST
depends on SND
depends on MTD
select I2C_ALGOBIT
select VIDEO_ADV7604
select VIDEO_ADV7511

View file

@ -707,11 +707,7 @@ static inline int cx18_raw_vbi(const struct cx18 *cx)
/* Call the specified callback for all subdevs with a grp_id bit matching the
* mask in hw (if 0, then match them all). Ignore any errors. */
#define cx18_call_hw(cx, hw, o, f, args...) \
do { \
struct v4l2_subdev *__sd; \
__v4l2_device_call_subdevs_p(&(cx)->v4l2_dev, __sd, \
!(hw) || (__sd->grp_id & (hw)), o, f , ##args); \
} while (0)
v4l2_device_mask_call_all(&(cx)->v4l2_dev, hw, o, f, ##args)
#define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args)
@ -719,12 +715,7 @@ static inline int cx18_raw_vbi(const struct cx18 *cx)
* mask in hw (if 0, then match them all). If the callback returns an error
* other than 0 or -ENOIOCTLCMD, then return with that error code. */
#define cx18_call_hw_err(cx, hw, o, f, args...) \
({ \
struct v4l2_subdev *__sd; \
__v4l2_device_call_subdevs_until_err_p(&(cx)->v4l2_dev, \
__sd, !(hw) || (__sd->grp_id & (hw)), o, f, \
##args); \
})
v4l2_device_mask_call_until_err(&(cx)->v4l2_dev, hw, o, f, ##args)
#define cx18_call_all_err(cx, o, f, args...) \
cx18_call_hw_err(cx, 0, o, f , ##args)

View file

@ -24,7 +24,7 @@ void cx23885_av_work_handler(struct work_struct *work)
{
struct cx23885_dev *dev =
container_of(work, struct cx23885_dev, cx25840_work);
bool handled;
bool handled = false;
v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine,
PCI_MSK_AV_CORE, &handled);

View file

@ -827,12 +827,7 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv)
/* Call the specified callback for all subdevs matching hw (if 0, then
match them all). Ignore any errors. */
#define ivtv_call_hw(itv, hw, o, f, args...) \
do { \
struct v4l2_subdev *__sd; \
__v4l2_device_call_subdevs_p(&(itv)->v4l2_dev, __sd, \
!(hw) ? true : (__sd->grp_id & (hw)), \
o, f, ##args); \
} while (0)
v4l2_device_mask_call_all(&(itv)->v4l2_dev, hw, o, f, ##args)
#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
@ -840,11 +835,7 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv)
match them all). If the callback returns an error other than 0 or
-ENOIOCTLCMD, then return with that error code. */
#define ivtv_call_hw_err(itv, hw, o, f, args...) \
({ \
struct v4l2_subdev *__sd; \
__v4l2_device_call_subdevs_until_err_p(&(itv)->v4l2_dev, __sd, \
!(hw) || (__sd->grp_id & (hw)), o, f , ##args); \
})
v4l2_device_mask_call_until_err(&(itv)->v4l2_dev, hw, o, f, ##args)
#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)

View file

@ -203,7 +203,7 @@ int smi_ir_init(struct smi_dev *dev)
rc_dev->dev.parent = &dev->pci_dev->dev;
rc_dev->driver_type = RC_DRIVER_SCANCODE;
rc_dev->map_name = RC_MAP_DVBSKY;
rc_dev->map_name = dev->info->rc_map;
ir->rc_dev = rc_dev;
ir->dev = dev;

View file

@ -716,7 +716,8 @@ static int smi_fe_init(struct smi_port *port)
/* init MAC.*/
ret = smi_read_eeprom(&dev->i2c_bus[0], 0xc0, mac_ee, 16);
dev_info(&port->dev->pci_dev->dev,
"DVBSky SMI PCIe MAC= %pM\n", mac_ee + (port->idx)*8);
"%s port %d MAC: %pM\n", dev->info->name,
port->idx, mac_ee + (port->idx)*8);
memcpy(adap->proposed_mac, mac_ee + (port->idx)*8, 6);
return ret;
}
@ -1066,6 +1067,7 @@ static struct smi_cfg_info dvbsky_s950_cfg = {
.ts_1 = SMI_TS_DMA_BOTH,
.fe_0 = DVBSKY_FE_NULL,
.fe_1 = DVBSKY_FE_M88DS3103,
.rc_map = RC_MAP_DVBSKY,
};
static struct smi_cfg_info dvbsky_s952_cfg = {
@ -1075,6 +1077,7 @@ static struct smi_cfg_info dvbsky_s952_cfg = {
.ts_1 = SMI_TS_DMA_BOTH,
.fe_0 = DVBSKY_FE_M88RS6000,
.fe_1 = DVBSKY_FE_M88RS6000,
.rc_map = RC_MAP_DVBSKY,
};
static struct smi_cfg_info dvbsky_t9580_cfg = {
@ -1084,6 +1087,17 @@ static struct smi_cfg_info dvbsky_t9580_cfg = {
.ts_1 = SMI_TS_DMA_BOTH,
.fe_0 = DVBSKY_FE_SIT2,
.fe_1 = DVBSKY_FE_M88DS3103,
.rc_map = RC_MAP_DVBSKY,
};
static struct smi_cfg_info technotrend_s2_4200_cfg = {
.type = SMI_TECHNOTREND_S2_4200,
.name = "TechnoTrend TT-budget S2-4200 Twin",
.ts_0 = SMI_TS_DMA_BOTH,
.ts_1 = SMI_TS_DMA_BOTH,
.fe_0 = DVBSKY_FE_M88RS6000,
.fe_1 = DVBSKY_FE_M88RS6000,
.rc_map = RC_MAP_TT_1500,
};
/* PCI IDs */
@ -1096,6 +1110,7 @@ static const struct pci_device_id smi_id_table[] = {
SMI_ID(0x4254, 0x0550, dvbsky_s950_cfg),
SMI_ID(0x4254, 0x0552, dvbsky_s952_cfg),
SMI_ID(0x4254, 0x5580, dvbsky_t9580_cfg),
SMI_ID(0x13c2, 0x3016, technotrend_s2_4200_cfg),
{0}
};
MODULE_DEVICE_TABLE(pci, smi_id_table);

View file

@ -216,6 +216,7 @@ struct smi_cfg_info {
#define SMI_DVBSKY_S950 1
#define SMI_DVBSKY_T9580 2
#define SMI_DVBSKY_T982 3
#define SMI_TECHNOTREND_S2_4200 4
int type;
char *name;
#define SMI_TS_NULL 0
@ -232,6 +233,7 @@ struct smi_cfg_info {
#define DVBSKY_FE_SIT2 3
int fe_0;
int fe_1;
char *rc_map;
};
struct smi_rc {

View file

@ -444,27 +444,19 @@ static int vidioc_querycap(struct file *file, void *priv,
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std)
{
struct sta2x11_vip *vip = video_drvdata(file);
v4l2_std_id oldstd = vip->std, newstd;
int status;
if (V4L2_STD_ALL == std) {
v4l2_subdev_call(vip->decoder, video, s_std, std);
ssleep(2);
v4l2_subdev_call(vip->decoder, video, querystd, &newstd);
v4l2_subdev_call(vip->decoder, video, g_input_status, &status);
if (status & V4L2_IN_ST_NO_SIGNAL)
/*
* This is here for backwards compatibility only.
* The use of V4L2_STD_ALL to trigger a querystd is non-standard.
*/
if (std == V4L2_STD_ALL) {
v4l2_subdev_call(vip->decoder, video, querystd, &std);
if (std == V4L2_STD_UNKNOWN)
return -EIO;
std = vip->std = newstd;
if (oldstd != std) {
if (V4L2_STD_525_60 & std)
vip->format = formats_60[0];
else
vip->format = formats_50[0];
}
return 0;
}
if (oldstd != std) {
if (vip->std != std) {
vip->std = std;
if (V4L2_STD_525_60 & std)
vip->format = formats_60[0];
else

View file

@ -0,0 +1,18 @@
config VIDEO_TW686X
tristate "Intersil/Techwell TW686x video capture cards"
depends on PCI && VIDEO_DEV && VIDEO_V4L2 && SND
depends on HAS_DMA
select VIDEOBUF2_VMALLOC
select SND_PCM
help
Support for Intersil/Techwell TW686x-based frame grabber cards.
Currently supported chips:
- TW6864 (4 video channels),
- TW6865 (4 video channels, not tested, second generation chip),
- TW6868 (8 video channels but only 4 first channels using
built-in video decoder are supported, not tested),
- TW6869 (8 video channels, second generation chip).
To compile this driver as a module, choose M here: the module
will be named tw686x.

View file

@ -0,0 +1,3 @@
tw686x-objs := tw686x-core.o tw686x-video.o tw686x-audio.o
obj-$(CONFIG_VIDEO_TW686X) += tw686x.o

View file

@ -0,0 +1,386 @@
/*
* Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar
*
* Based on the audio support from the tw6869 driver:
* Copyright 2015 www.starterkit.ru <info@starterkit.ru>
*
* Based on:
* Driver for Intersil|Techwell TW6869 based DVR cards
* (c) 2011-12 liran <jli11@intersil.com> [Intersil|Techwell China]
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/control.h>
#include "tw686x.h"
#include "tw686x-regs.h"
#define AUDIO_CHANNEL_OFFSET 8
void tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests,
unsigned int pb_status)
{
unsigned long flags;
unsigned int ch, pb;
for_each_set_bit(ch, &requests, max_channels(dev)) {
struct tw686x_audio_channel *ac = &dev->audio_channels[ch];
struct tw686x_audio_buf *done = NULL;
struct tw686x_audio_buf *next = NULL;
struct tw686x_dma_desc *desc;
pb = !!(pb_status & BIT(AUDIO_CHANNEL_OFFSET + ch));
spin_lock_irqsave(&ac->lock, flags);
/* Sanity check */
if (!ac->ss || !ac->curr_bufs[0] || !ac->curr_bufs[1]) {
spin_unlock_irqrestore(&ac->lock, flags);
continue;
}
if (!list_empty(&ac->buf_list)) {
next = list_first_entry(&ac->buf_list,
struct tw686x_audio_buf, list);
list_move_tail(&next->list, &ac->buf_list);
done = ac->curr_bufs[!pb];
ac->curr_bufs[pb] = next;
}
spin_unlock_irqrestore(&ac->lock, flags);
desc = &ac->dma_descs[pb];
if (done && next && desc->virt) {
memcpy(done->virt, desc->virt, desc->size);
ac->ptr = done->dma - ac->buf[0].dma;
snd_pcm_period_elapsed(ac->ss);
}
}
}
static int tw686x_pcm_hw_params(struct snd_pcm_substream *ss,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params));
}
static int tw686x_pcm_hw_free(struct snd_pcm_substream *ss)
{
return snd_pcm_lib_free_pages(ss);
}
/*
* The audio device rate is global and shared among all
* capture channels. The driver makes no effort to prevent
* rate modifications. User is free change the rate, but it
* means changing the rate for all capture sub-devices.
*/
static const struct snd_pcm_hardware tw686x_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 1,
.buffer_bytes_max = TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ,
.period_bytes_min = TW686X_AUDIO_PAGE_SZ,
.period_bytes_max = TW686X_AUDIO_PAGE_SZ,
.periods_min = TW686X_AUDIO_PERIODS_MIN,
.periods_max = TW686X_AUDIO_PERIODS_MAX,
};
static int tw686x_pcm_open(struct snd_pcm_substream *ss)
{
struct tw686x_dev *dev = snd_pcm_substream_chip(ss);
struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];
struct snd_pcm_runtime *rt = ss->runtime;
int err;
ac->ss = ss;
rt->hw = tw686x_capture_hw;
err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
if (err < 0)
return err;
return 0;
}
static int tw686x_pcm_close(struct snd_pcm_substream *ss)
{
struct tw686x_dev *dev = snd_pcm_substream_chip(ss);
struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];
ac->ss = NULL;
return 0;
}
static int tw686x_pcm_prepare(struct snd_pcm_substream *ss)
{
struct tw686x_dev *dev = snd_pcm_substream_chip(ss);
struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];
struct snd_pcm_runtime *rt = ss->runtime;
unsigned int period_size = snd_pcm_lib_period_bytes(ss);
struct tw686x_audio_buf *p_buf, *b_buf;
unsigned long flags;
int i;
spin_lock_irqsave(&dev->lock, flags);
tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch);
spin_unlock_irqrestore(&dev->lock, flags);
if (dev->audio_rate != rt->rate) {
u32 reg;
dev->audio_rate = rt->rate;
reg = ((125000000 / rt->rate) << 16) +
((125000000 % rt->rate) << 16) / rt->rate;
reg_write(dev, AUDIO_CONTROL2, reg);
}
if (period_size != TW686X_AUDIO_PAGE_SZ ||
rt->periods < TW686X_AUDIO_PERIODS_MIN ||
rt->periods > TW686X_AUDIO_PERIODS_MAX) {
return -EINVAL;
}
spin_lock_irqsave(&ac->lock, flags);
INIT_LIST_HEAD(&ac->buf_list);
for (i = 0; i < rt->periods; i++) {
ac->buf[i].dma = rt->dma_addr + period_size * i;
ac->buf[i].virt = rt->dma_area + period_size * i;
INIT_LIST_HEAD(&ac->buf[i].list);
list_add_tail(&ac->buf[i].list, &ac->buf_list);
}
p_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list);
list_move_tail(&p_buf->list, &ac->buf_list);
b_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list);
list_move_tail(&b_buf->list, &ac->buf_list);
ac->curr_bufs[0] = p_buf;
ac->curr_bufs[1] = b_buf;
ac->ptr = 0;
spin_unlock_irqrestore(&ac->lock, flags);
return 0;
}
static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
{
struct tw686x_dev *dev = snd_pcm_substream_chip(ss);
struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];
unsigned long flags;
int err = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (ac->curr_bufs[0] && ac->curr_bufs[1]) {
spin_lock_irqsave(&dev->lock, flags);
tw686x_enable_channel(dev,
AUDIO_CHANNEL_OFFSET + ac->ch);
spin_unlock_irqrestore(&dev->lock, flags);
mod_timer(&dev->dma_delay_timer,
jiffies + msecs_to_jiffies(100));
} else {
err = -EIO;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
spin_lock_irqsave(&dev->lock, flags);
tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch);
spin_unlock_irqrestore(&dev->lock, flags);
spin_lock_irqsave(&ac->lock, flags);
ac->curr_bufs[0] = NULL;
ac->curr_bufs[1] = NULL;
spin_unlock_irqrestore(&ac->lock, flags);
break;
default:
err = -EINVAL;
}
return err;
}
static snd_pcm_uframes_t tw686x_pcm_pointer(struct snd_pcm_substream *ss)
{
struct tw686x_dev *dev = snd_pcm_substream_chip(ss);
struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];
return bytes_to_frames(ss->runtime, ac->ptr);
}
static struct snd_pcm_ops tw686x_pcm_ops = {
.open = tw686x_pcm_open,
.close = tw686x_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = tw686x_pcm_hw_params,
.hw_free = tw686x_pcm_hw_free,
.prepare = tw686x_pcm_prepare,
.trigger = tw686x_pcm_trigger,
.pointer = tw686x_pcm_pointer,
};
static int tw686x_snd_pcm_init(struct tw686x_dev *dev)
{
struct snd_card *card = dev->snd_card;
struct snd_pcm *pcm;
struct snd_pcm_substream *ss;
unsigned int i;
int err;
err = snd_pcm_new(card, card->driver, 0, 0, max_channels(dev), &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tw686x_pcm_ops);
snd_pcm_chip(pcm) = dev;
pcm->info_flags = 0;
strlcpy(pcm->name, "tw686x PCM", sizeof(pcm->name));
for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
ss; ss = ss->next, i++)
snprintf(ss->name, sizeof(ss->name), "vch%u audio", i);
return snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(dev->pci_dev),
TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ,
TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ);
}
static void tw686x_audio_dma_free(struct tw686x_dev *dev,
struct tw686x_audio_channel *ac)
{
int pb;
for (pb = 0; pb < 2; pb++) {
if (!ac->dma_descs[pb].virt)
continue;
pci_free_consistent(dev->pci_dev, ac->dma_descs[pb].size,
ac->dma_descs[pb].virt,
ac->dma_descs[pb].phys);
ac->dma_descs[pb].virt = NULL;
}
}
static int tw686x_audio_dma_alloc(struct tw686x_dev *dev,
struct tw686x_audio_channel *ac)
{
int pb;
for (pb = 0; pb < 2; pb++) {
u32 reg = pb ? ADMA_B_ADDR[ac->ch] : ADMA_P_ADDR[ac->ch];
void *virt;
virt = pci_alloc_consistent(dev->pci_dev, TW686X_AUDIO_PAGE_SZ,
&ac->dma_descs[pb].phys);
if (!virt) {
dev_err(&dev->pci_dev->dev,
"dma%d: unable to allocate audio DMA %s-buffer\n",
ac->ch, pb ? "B" : "P");
return -ENOMEM;
}
ac->dma_descs[pb].virt = virt;
ac->dma_descs[pb].size = TW686X_AUDIO_PAGE_SZ;
reg_write(dev, reg, ac->dma_descs[pb].phys);
}
return 0;
}
void tw686x_audio_free(struct tw686x_dev *dev)
{
unsigned long flags;
u32 dma_ch_mask;
u32 dma_cmd;
spin_lock_irqsave(&dev->lock, flags);
dma_cmd = reg_read(dev, DMA_CMD);
dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE);
reg_write(dev, DMA_CMD, dma_cmd & ~0xff00);
reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask & ~0xff00);
spin_unlock_irqrestore(&dev->lock, flags);
if (!dev->snd_card)
return;
snd_card_free(dev->snd_card);
dev->snd_card = NULL;
}
int tw686x_audio_init(struct tw686x_dev *dev)
{
struct pci_dev *pci_dev = dev->pci_dev;
struct snd_card *card;
int err, ch;
/*
* AUDIO_CONTROL1
* DMA byte length [31:19] = 4096 (i.e. ALSA period)
* External audio enable [0] = enabled
*/
reg_write(dev, AUDIO_CONTROL1, 0x80000001);
err = snd_card_new(&pci_dev->dev, SNDRV_DEFAULT_IDX1,
SNDRV_DEFAULT_STR1,
THIS_MODULE, 0, &card);
if (err < 0)
return err;
dev->snd_card = card;
strlcpy(card->driver, "tw686x", sizeof(card->driver));
strlcpy(card->shortname, "tw686x", sizeof(card->shortname));
strlcpy(card->longname, pci_name(pci_dev), sizeof(card->longname));
snd_card_set_dev(card, &pci_dev->dev);
for (ch = 0; ch < max_channels(dev); ch++) {
struct tw686x_audio_channel *ac;
ac = &dev->audio_channels[ch];
spin_lock_init(&ac->lock);
ac->dev = dev;
ac->ch = ch;
err = tw686x_audio_dma_alloc(dev, ac);
if (err < 0)
goto err_cleanup;
}
err = tw686x_snd_pcm_init(dev);
if (err < 0)
goto err_cleanup;
err = snd_card_register(card);
if (!err)
return 0;
err_cleanup:
for (ch = 0; ch < max_channels(dev); ch++) {
if (!dev->audio_channels[ch].dev)
continue;
tw686x_audio_dma_free(dev, &dev->audio_channels[ch]);
}
snd_card_free(card);
dev->snd_card = NULL;
return err;
}

View file

@ -0,0 +1,415 @@
/*
* Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar
*
* Based on original driver by Krzysztof Ha?asa:
* Copyright (C) 2015 Industrial Research Institute for Automation
* and Measurements PIAP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* Notes
* -----
*
* 1. Under stress-testing, it has been observed that the PCIe link
* goes down, without reason. Therefore, the driver takes special care
* to allow device hot-unplugging.
*
* 2. TW686X devices are capable of setting a few different DMA modes,
* including: scatter-gather, field and frame modes. However,
* under stress testings it has been found that the machine can
* freeze completely if DMA registers are programmed while streaming
* is active.
* This driver tries to access hardware registers as infrequently
* as possible by:
* i. allocating fixed DMA buffers and memcpy'ing into
* vmalloc'ed buffers
* ii. using a timer to mitigate the rate of DMA reset operations,
* on DMA channels error.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include "tw686x.h"
#include "tw686x-regs.h"
/*
* This module parameter allows to control the DMA_TIMER_INTERVAL value.
* The DMA_TIMER_INTERVAL register controls the minimum DMA interrupt
* time span (iow, the maximum DMA interrupt rate) thus allowing for
* IRQ coalescing.
*
* The chip datasheet does not mention a time unit for this value, so
* users wanting fine-grain control over the interrupt rate should
* determine the desired value through testing.
*/
static u32 dma_interval = 0x00098968;
module_param(dma_interval, int, 0444);
MODULE_PARM_DESC(dma_interval, "Minimum time span for DMA interrupting host");
void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel)
{
u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE);
u32 dma_cmd = reg_read(dev, DMA_CMD);
dma_en &= ~BIT(channel);
dma_cmd &= ~BIT(channel);
/* Must remove it from pending too */
dev->pending_dma_en &= ~BIT(channel);
dev->pending_dma_cmd &= ~BIT(channel);
/* Stop DMA if no channels are enabled */
if (!dma_en)
dma_cmd = 0;
reg_write(dev, DMA_CHANNEL_ENABLE, dma_en);
reg_write(dev, DMA_CMD, dma_cmd);
}
void tw686x_enable_channel(struct tw686x_dev *dev, unsigned int channel)
{
u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE);
u32 dma_cmd = reg_read(dev, DMA_CMD);
dev->pending_dma_en |= dma_en | BIT(channel);
dev->pending_dma_cmd |= dma_cmd | DMA_CMD_ENABLE | BIT(channel);
}
/*
* The purpose of this awful hack is to avoid enabling the DMA
* channels "too fast" which makes some TW686x devices very
* angry and freeze the CPU (see note 1).
*/
static void tw686x_dma_delay(unsigned long data)
{
struct tw686x_dev *dev = (struct tw686x_dev *)data;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
reg_write(dev, DMA_CHANNEL_ENABLE, dev->pending_dma_en);
reg_write(dev, DMA_CMD, dev->pending_dma_cmd);
dev->pending_dma_en = 0;
dev->pending_dma_cmd = 0;
spin_unlock_irqrestore(&dev->lock, flags);
}
static void tw686x_reset_channels(struct tw686x_dev *dev, unsigned int ch_mask)
{
u32 dma_en, dma_cmd;
dma_en = reg_read(dev, DMA_CHANNEL_ENABLE);
dma_cmd = reg_read(dev, DMA_CMD);
/*
* Save pending register status, the timer will
* restore them.
*/
dev->pending_dma_en |= dma_en;
dev->pending_dma_cmd |= dma_cmd;
/* Disable the reset channels */
reg_write(dev, DMA_CHANNEL_ENABLE, dma_en & ~ch_mask);
if ((dma_en & ~ch_mask) == 0) {
dev_dbg(&dev->pci_dev->dev, "reset: stopping DMA\n");
dma_cmd &= ~DMA_CMD_ENABLE;
}
reg_write(dev, DMA_CMD, dma_cmd & ~ch_mask);
}
static irqreturn_t tw686x_irq(int irq, void *dev_id)
{
struct tw686x_dev *dev = (struct tw686x_dev *)dev_id;
unsigned int video_requests, audio_requests, reset_ch;
u32 fifo_status, fifo_signal, fifo_ov, fifo_bad, fifo_errors;
u32 int_status, dma_en, video_en, pb_status;
unsigned long flags;
int_status = reg_read(dev, INT_STATUS); /* cleared on read */
fifo_status = reg_read(dev, VIDEO_FIFO_STATUS);
/* INT_STATUS does not include FIFO_STATUS errors! */
if (!int_status && !TW686X_FIFO_ERROR(fifo_status))
return IRQ_NONE;
if (int_status & INT_STATUS_DMA_TOUT) {
dev_dbg(&dev->pci_dev->dev,
"DMA timeout. Resetting DMA for all channels\n");
reset_ch = ~0;
goto reset_channels;
}
spin_lock_irqsave(&dev->lock, flags);
dma_en = reg_read(dev, DMA_CHANNEL_ENABLE);
spin_unlock_irqrestore(&dev->lock, flags);
video_en = dma_en & 0xff;
fifo_signal = ~(fifo_status & 0xff) & video_en;
fifo_ov = fifo_status >> 24;
fifo_bad = fifo_status >> 16;
/* Mask of channels with signal and FIFO errors */
fifo_errors = fifo_signal & (fifo_ov | fifo_bad);
reset_ch = 0;
pb_status = reg_read(dev, PB_STATUS);
/* Coalesce video frame/error events */
video_requests = (int_status & video_en) | fifo_errors;
audio_requests = (int_status & dma_en) >> 8;
if (video_requests)
tw686x_video_irq(dev, video_requests, pb_status,
fifo_status, &reset_ch);
if (audio_requests)
tw686x_audio_irq(dev, audio_requests, pb_status);
reset_channels:
if (reset_ch) {
spin_lock_irqsave(&dev->lock, flags);
tw686x_reset_channels(dev, reset_ch);
spin_unlock_irqrestore(&dev->lock, flags);
mod_timer(&dev->dma_delay_timer,
jiffies + msecs_to_jiffies(100));
}
return IRQ_HANDLED;
}
static void tw686x_dev_release(struct v4l2_device *v4l2_dev)
{
struct tw686x_dev *dev = container_of(v4l2_dev, struct tw686x_dev,
v4l2_dev);
unsigned int ch;
for (ch = 0; ch < max_channels(dev); ch++)
v4l2_ctrl_handler_free(&dev->video_channels[ch].ctrl_handler);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev->audio_channels);
kfree(dev->video_channels);
kfree(dev);
}
static int tw686x_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
struct tw686x_dev *dev;
int err;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->type = pci_id->driver_data;
sprintf(dev->name, "tw%04X", pci_dev->device);
dev->video_channels = kcalloc(max_channels(dev),
sizeof(*dev->video_channels), GFP_KERNEL);
if (!dev->video_channels) {
err = -ENOMEM;
goto free_dev;
}
dev->audio_channels = kcalloc(max_channels(dev),
sizeof(*dev->audio_channels), GFP_KERNEL);
if (!dev->audio_channels) {
err = -ENOMEM;
goto free_video;
}
pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name,
pci_name(pci_dev), pci_dev->irq,
(unsigned long)pci_resource_start(pci_dev, 0));
dev->pci_dev = pci_dev;
if (pci_enable_device(pci_dev)) {
err = -EIO;
goto free_audio;
}
pci_set_master(pci_dev);
err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
if (err) {
dev_err(&pci_dev->dev, "32-bit PCI DMA not supported\n");
err = -EIO;
goto disable_pci;
}
err = pci_request_regions(pci_dev, dev->name);
if (err) {
dev_err(&pci_dev->dev, "unable to request PCI region\n");
goto disable_pci;
}
dev->mmio = pci_ioremap_bar(pci_dev, 0);
if (!dev->mmio) {
dev_err(&pci_dev->dev, "unable to remap PCI region\n");
err = -ENOMEM;
goto free_region;
}
/* Reset all subsystems */
reg_write(dev, SYS_SOFT_RST, 0x0f);
mdelay(1);
reg_write(dev, SRST[0], 0x3f);
if (max_channels(dev) > 4)
reg_write(dev, SRST[1], 0x3f);
/* Disable the DMA engine */
reg_write(dev, DMA_CMD, 0);
reg_write(dev, DMA_CHANNEL_ENABLE, 0);
/* Enable DMA FIFO overflow and pointer check */
reg_write(dev, DMA_CONFIG, 0xffffff04);
reg_write(dev, DMA_CHANNEL_TIMEOUT, 0x140c8584);
reg_write(dev, DMA_TIMER_INTERVAL, dma_interval);
spin_lock_init(&dev->lock);
err = request_irq(pci_dev->irq, tw686x_irq, IRQF_SHARED,
dev->name, dev);
if (err < 0) {
dev_err(&pci_dev->dev, "unable to request interrupt\n");
goto iounmap;
}
setup_timer(&dev->dma_delay_timer,
tw686x_dma_delay, (unsigned long) dev);
/*
* This must be set right before initializing v4l2_dev.
* It's used to release resources after the last handle
* held is released.
*/
dev->v4l2_dev.release = tw686x_dev_release;
err = tw686x_video_init(dev);
if (err) {
dev_err(&pci_dev->dev, "can't register video\n");
goto free_irq;
}
err = tw686x_audio_init(dev);
if (err)
dev_warn(&pci_dev->dev, "can't register audio\n");
pci_set_drvdata(pci_dev, dev);
return 0;
free_irq:
free_irq(pci_dev->irq, dev);
iounmap:
pci_iounmap(pci_dev, dev->mmio);
free_region:
pci_release_regions(pci_dev);
disable_pci:
pci_disable_device(pci_dev);
free_audio:
kfree(dev->audio_channels);
free_video:
kfree(dev->video_channels);
free_dev:
kfree(dev);
return err;
}
static void tw686x_remove(struct pci_dev *pci_dev)
{
struct tw686x_dev *dev = pci_get_drvdata(pci_dev);
unsigned long flags;
/* This guarantees the IRQ handler is no longer running,
* which means we can kiss good-bye some resources.
*/
free_irq(pci_dev->irq, dev);
tw686x_video_free(dev);
tw686x_audio_free(dev);
del_timer_sync(&dev->dma_delay_timer);
pci_iounmap(pci_dev, dev->mmio);
pci_release_regions(pci_dev);
pci_disable_device(pci_dev);
/*
* Setting pci_dev to NULL allows to detect hardware is no longer
* available and will be used by vb2_ops. This is required because
* the device sometimes hot-unplugs itself as the result of a PCIe
* link down.
* The lock is really important here.
*/
spin_lock_irqsave(&dev->lock, flags);
dev->pci_dev = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
/*
* This calls tw686x_dev_release if it's the last reference.
* Otherwise, release is postponed until there are no users left.
*/
v4l2_device_put(&dev->v4l2_dev);
}
/*
* On TW6864 and TW6868, all channels share the pair of video DMA SG tables,
* with 10-bit start_idx and end_idx determining start and end of frame buffer
* for particular channel.
* TW6868 with all its 8 channels would be problematic (only 127 SG entries per
* channel) but we support only 4 channels on this chip anyway (the first
* 4 channels are driven with internal video decoder, the other 4 would require
* an external TW286x part).
*
* On TW6865 and TW6869, each channel has its own DMA SG table, with indexes
* starting with 0. Both chips have complete sets of internal video decoders
* (respectively 4 or 8-channel).
*
* All chips have separate SG tables for two video frames.
*/
/* driver_data is number of A/V channels */
static const struct pci_device_id tw686x_pci_tbl[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6864),
.driver_data = 4
},
{
PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6865), /* not tested */
.driver_data = 4 | TYPE_SECOND_GEN
},
/*
* TW6868 supports 8 A/V channels with an external TW2865 chip;
* not supported by the driver.
*/
{
PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6868), /* not tested */
.driver_data = 4
},
{
PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6869),
.driver_data = 8 | TYPE_SECOND_GEN},
{}
};
MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl);
static struct pci_driver tw686x_pci_driver = {
.name = "tw686x",
.id_table = tw686x_pci_tbl,
.probe = tw686x_probe,
.remove = tw686x_remove,
};
module_pci_driver(tw686x_pci_driver);
MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]");
MODULE_AUTHOR("Ezequiel Garcia <[email protected]>");
MODULE_AUTHOR("Krzysztof Ha?asa <[email protected]>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,122 @@
/* DMA controller registers */
#define REG8_1(a0) ((const u16[8]) { a0, a0 + 1, a0 + 2, a0 + 3, \
a0 + 4, a0 + 5, a0 + 6, a0 + 7})
#define REG8_2(a0) ((const u16[8]) { a0, a0 + 2, a0 + 4, a0 + 6, \
a0 + 8, a0 + 0xa, a0 + 0xc, a0 + 0xe})
#define REG8_8(a0) ((const u16[8]) { a0, a0 + 8, a0 + 0x10, a0 + 0x18, \
a0 + 0x20, a0 + 0x28, a0 + 0x30, \
a0 + 0x38})
#define INT_STATUS 0x00
#define PB_STATUS 0x01
#define DMA_CMD 0x02
#define VIDEO_FIFO_STATUS 0x03
#define VIDEO_CHANNEL_ID 0x04
#define VIDEO_PARSER_STATUS 0x05
#define SYS_SOFT_RST 0x06
#define DMA_PAGE_TABLE0_ADDR ((const u16[8]) { 0x08, 0xd0, 0xd2, 0xd4, \
0xd6, 0xd8, 0xda, 0xdc })
#define DMA_PAGE_TABLE1_ADDR ((const u16[8]) { 0x09, 0xd1, 0xd3, 0xd5, \
0xd7, 0xd9, 0xdb, 0xdd })
#define DMA_CHANNEL_ENABLE 0x0a
#define DMA_CONFIG 0x0b
#define DMA_TIMER_INTERVAL 0x0c
#define DMA_CHANNEL_TIMEOUT 0x0d
#define VDMA_CHANNEL_CONFIG REG8_1(0x10)
#define ADMA_P_ADDR REG8_2(0x18)
#define ADMA_B_ADDR REG8_2(0x19)
#define DMA10_P_ADDR 0x28
#define DMA10_B_ADDR 0x29
#define VIDEO_CONTROL1 0x2a
#define VIDEO_CONTROL2 0x2b
#define AUDIO_CONTROL1 0x2c
#define AUDIO_CONTROL2 0x2d
#define PHASE_REF 0x2e
#define GPIO_REG 0x2f
#define INTL_HBAR_CTRL REG8_1(0x30)
#define AUDIO_CONTROL3 0x38
#define VIDEO_FIELD_CTRL REG8_1(0x39)
#define HSCALER_CTRL REG8_1(0x42)
#define VIDEO_SIZE REG8_1(0x4A)
#define VIDEO_SIZE_F2 REG8_1(0x52)
#define MD_CONF REG8_1(0x60)
#define MD_INIT REG8_1(0x68)
#define MD_MAP0 REG8_1(0x70)
#define VDMA_P_ADDR REG8_8(0x80) /* not used in DMA SG mode */
#define VDMA_WHP REG8_8(0x81)
#define VDMA_B_ADDR REG8_8(0x82)
#define VDMA_F2_P_ADDR REG8_8(0x84)
#define VDMA_F2_WHP REG8_8(0x85)
#define VDMA_F2_B_ADDR REG8_8(0x86)
#define EP_REG_ADDR 0xfe
#define EP_REG_DATA 0xff
/* Video decoder registers */
#define VDREG8(a0) ((const u16[8]) { \
a0 + 0x000, a0 + 0x010, a0 + 0x020, a0 + 0x030, \
a0 + 0x100, a0 + 0x110, a0 + 0x120, a0 + 0x130})
#define VIDSTAT VDREG8(0x100)
#define BRIGHT VDREG8(0x101)
#define CONTRAST VDREG8(0x102)
#define SHARPNESS VDREG8(0x103)
#define SAT_U VDREG8(0x104)
#define SAT_V VDREG8(0x105)
#define HUE VDREG8(0x106)
#define CROP_HI VDREG8(0x107)
#define VDELAY_LO VDREG8(0x108)
#define VACTIVE_LO VDREG8(0x109)
#define HDELAY_LO VDREG8(0x10a)
#define HACTIVE_LO VDREG8(0x10b)
#define MVSN VDREG8(0x10c)
#define STATUS2 VDREG8(0x10d)
#define SDT VDREG8(0x10e)
#define SDT_EN VDREG8(0x10f)
#define VSCALE_LO VDREG8(0x144)
#define SCALE_HI VDREG8(0x145)
#define HSCALE_LO VDREG8(0x146)
#define F2CROP_HI VDREG8(0x147)
#define F2VDELAY_LO VDREG8(0x148)
#define F2VACTIVE_LO VDREG8(0x149)
#define F2HDELAY_LO VDREG8(0x14a)
#define F2HACTIVE_LO VDREG8(0x14b)
#define F2VSCALE_LO VDREG8(0x14c)
#define F2SCALE_HI VDREG8(0x14d)
#define F2HSCALE_LO VDREG8(0x14e)
#define F2CNT VDREG8(0x14f)
#define VDREG2(a0) ((const u16[2]) { a0, a0 + 0x100 })
#define SRST VDREG2(0x180)
#define ACNTL VDREG2(0x181)
#define ACNTL2 VDREG2(0x182)
#define CNTRL1 VDREG2(0x183)
#define CKHY VDREG2(0x184)
#define SHCOR VDREG2(0x185)
#define CORING VDREG2(0x186)
#define CLMPG VDREG2(0x187)
#define IAGC VDREG2(0x188)
#define VCTRL1 VDREG2(0x18f)
#define MISC1 VDREG2(0x194)
#define LOOP VDREG2(0x195)
#define MISC2 VDREG2(0x196)
#define CLMD VDREG2(0x197)
#define ANPWRDOWN VDREG2(0x1ce)
#define AIGAIN ((const u16[8]) { 0x1d0, 0x1d1, 0x1d2, 0x1d3, \
0x2d0, 0x2d1, 0x2d2, 0x2d3 })
#define SYS_MODE_DMA_SHIFT 13
#define DMA_CMD_ENABLE BIT(31)
#define INT_STATUS_DMA_TOUT BIT(17)
#define TW686X_VIDSTAT_HLOCK BIT(6)
#define TW686X_VIDSTAT_VDLOSS BIT(7)
#define TW686X_STD_NTSC_M 0
#define TW686X_STD_PAL 1
#define TW686X_STD_SECAM 2
#define TW686X_STD_NTSC_443 3
#define TW686X_STD_PAL_M 4
#define TW686X_STD_PAL_CN 5
#define TW686X_STD_PAL_60 6
#define TW686X_FIFO_ERROR(x) (x & ~(0xff))

View file

@ -0,0 +1,937 @@
/*
* Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar
*
* Based on original driver by Krzysztof Ha?asa:
* Copyright (C) 2015 Industrial Research Institute for Automation
* and Measurements PIAP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-vmalloc.h>
#include "tw686x.h"
#include "tw686x-regs.h"
#define TW686X_INPUTS_PER_CH 4
#define TW686X_VIDEO_WIDTH 720
#define TW686X_VIDEO_HEIGHT(id) ((id & V4L2_STD_525_60) ? 480 : 576)
static const struct tw686x_format formats[] = {
{
.fourcc = V4L2_PIX_FMT_UYVY,
.mode = 0,
.depth = 16,
}, {
.fourcc = V4L2_PIX_FMT_RGB565,
.mode = 5,
.depth = 16,
}, {
.fourcc = V4L2_PIX_FMT_YUYV,
.mode = 6,
.depth = 16,
}
};
static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
{
static const unsigned int map[15] = {
0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041,
0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445,
0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555
};
static const unsigned int std_625_50[26] = {
0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7,
8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 0
};
static const unsigned int std_525_60[31] = {
0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 0, 0
};
unsigned int i;
if (std & V4L2_STD_525_60) {
if (fps >= ARRAY_SIZE(std_525_60))
fps = 30;
i = std_525_60[fps];
} else {
if (fps >= ARRAY_SIZE(std_625_50))
fps = 25;
i = std_625_50[fps];
}
return map[i];
}
static void tw686x_set_framerate(struct tw686x_video_channel *vc,
unsigned int fps)
{
unsigned int map;
if (vc->fps == fps)
return;
map = tw686x_fields_map(vc->video_standard, fps) << 1;
map |= map << 1;
if (map > 0)
map |= BIT(31);
reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], map);
vc->fps = fps;
}
static const struct tw686x_format *format_by_fourcc(unsigned int fourcc)
{
unsigned int cnt;
for (cnt = 0; cnt < ARRAY_SIZE(formats); cnt++)
if (formats[cnt].fourcc == fourcc)
return &formats[cnt];
return NULL;
}
static int tw686x_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
unsigned int szimage =
(vc->width * vc->height * vc->format->depth) >> 3;
/*
* Let's request at least three buffers: two for the
* DMA engine and one for userspace.
*/
if (vq->num_buffers + *nbuffers < 3)
*nbuffers = 3 - vq->num_buffers;
if (*nplanes) {
if (*nplanes != 1 || sizes[0] < szimage)
return -EINVAL;
return 0;
}
sizes[0] = szimage;
*nplanes = 1;
return 0;
}
static void tw686x_buf_queue(struct vb2_buffer *vb)
{
struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue);
struct tw686x_dev *dev = vc->dev;
struct pci_dev *pci_dev;
unsigned long flags;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct tw686x_v4l2_buf *buf =
container_of(vbuf, struct tw686x_v4l2_buf, vb);
/* Check device presence */
spin_lock_irqsave(&dev->lock, flags);
pci_dev = dev->pci_dev;
spin_unlock_irqrestore(&dev->lock, flags);
if (!pci_dev) {
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
return;
}
spin_lock_irqsave(&vc->qlock, flags);
list_add_tail(&buf->list, &vc->vidq_queued);
spin_unlock_irqrestore(&vc->qlock, flags);
}
/*
* We can call this even when alloc_dma failed for the given channel
*/
static void tw686x_free_dma(struct tw686x_video_channel *vc, unsigned int pb)
{
struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
struct tw686x_dev *dev = vc->dev;
struct pci_dev *pci_dev;
unsigned long flags;
/* Check device presence. Shouldn't really happen! */
spin_lock_irqsave(&dev->lock, flags);
pci_dev = dev->pci_dev;
spin_unlock_irqrestore(&dev->lock, flags);
if (!pci_dev) {
WARN(1, "trying to deallocate on missing device\n");
return;
}
if (desc->virt) {
pci_free_consistent(dev->pci_dev, desc->size,
desc->virt, desc->phys);
desc->virt = NULL;
}
}
static int tw686x_alloc_dma(struct tw686x_video_channel *vc, unsigned int pb)
{
struct tw686x_dev *dev = vc->dev;
u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch];
unsigned int len;
void *virt;
WARN(vc->dma_descs[pb].virt,
"Allocating buffer but previous still here\n");
len = (vc->width * vc->height * vc->format->depth) >> 3;
virt = pci_alloc_consistent(dev->pci_dev, len,
&vc->dma_descs[pb].phys);
if (!virt) {
v4l2_err(&dev->v4l2_dev,
"dma%d: unable to allocate %s-buffer\n",
vc->ch, pb ? "B" : "P");
return -ENOMEM;
}
vc->dma_descs[pb].size = len;
vc->dma_descs[pb].virt = virt;
reg_write(dev, reg, vc->dma_descs[pb].phys);
return 0;
}
static void tw686x_buffer_refill(struct tw686x_video_channel *vc,
unsigned int pb)
{
struct tw686x_v4l2_buf *buf;
while (!list_empty(&vc->vidq_queued)) {
buf = list_first_entry(&vc->vidq_queued,
struct tw686x_v4l2_buf, list);
list_del(&buf->list);
vc->curr_bufs[pb] = buf;
return;
}
vc->curr_bufs[pb] = NULL;
}
static void tw686x_clear_queue(struct tw686x_video_channel *vc,
enum vb2_buffer_state state)
{
unsigned int pb;
while (!list_empty(&vc->vidq_queued)) {
struct tw686x_v4l2_buf *buf;
buf = list_first_entry(&vc->vidq_queued,
struct tw686x_v4l2_buf, list);
list_del(&buf->list);
vb2_buffer_done(&buf->vb.vb2_buf, state);
}
for (pb = 0; pb < 2; pb++) {
if (vc->curr_bufs[pb])
vb2_buffer_done(&vc->curr_bufs[pb]->vb.vb2_buf, state);
vc->curr_bufs[pb] = NULL;
}
}
static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
struct tw686x_dev *dev = vc->dev;
struct pci_dev *pci_dev;
unsigned long flags;
int pb, err;
/* Check device presence */
spin_lock_irqsave(&dev->lock, flags);
pci_dev = dev->pci_dev;
spin_unlock_irqrestore(&dev->lock, flags);
if (!pci_dev) {
err = -ENODEV;
goto err_clear_queue;
}
spin_lock_irqsave(&vc->qlock, flags);
/* Sanity check */
if (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt) {
spin_unlock_irqrestore(&vc->qlock, flags);
v4l2_err(&dev->v4l2_dev,
"video%d: refusing to start without DMA buffers\n",
vc->num);
err = -ENOMEM;
goto err_clear_queue;
}
for (pb = 0; pb < 2; pb++)
tw686x_buffer_refill(vc, pb);
spin_unlock_irqrestore(&vc->qlock, flags);
vc->sequence = 0;
vc->pb = 0;
spin_lock_irqsave(&dev->lock, flags);
tw686x_enable_channel(dev, vc->ch);
spin_unlock_irqrestore(&dev->lock, flags);
mod_timer(&dev->dma_delay_timer, jiffies + msecs_to_jiffies(100));
return 0;
err_clear_queue:
spin_lock_irqsave(&vc->qlock, flags);
tw686x_clear_queue(vc, VB2_BUF_STATE_QUEUED);
spin_unlock_irqrestore(&vc->qlock, flags);
return err;
}
static void tw686x_stop_streaming(struct vb2_queue *vq)
{
struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
struct tw686x_dev *dev = vc->dev;
struct pci_dev *pci_dev;
unsigned long flags;
/* Check device presence */
spin_lock_irqsave(&dev->lock, flags);
pci_dev = dev->pci_dev;
spin_unlock_irqrestore(&dev->lock, flags);
if (pci_dev)
tw686x_disable_channel(dev, vc->ch);
spin_lock_irqsave(&vc->qlock, flags);
tw686x_clear_queue(vc, VB2_BUF_STATE_ERROR);
spin_unlock_irqrestore(&vc->qlock, flags);
}
static int tw686x_buf_prepare(struct vb2_buffer *vb)
{
struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue);
unsigned int size =
(vc->width * vc->height * vc->format->depth) >> 3;
if (vb2_plane_size(vb, 0) < size)
return -EINVAL;
vb2_set_plane_payload(vb, 0, size);
return 0;
}
static struct vb2_ops tw686x_video_qops = {
.queue_setup = tw686x_queue_setup,
.buf_queue = tw686x_buf_queue,
.buf_prepare = tw686x_buf_prepare,
.start_streaming = tw686x_start_streaming,
.stop_streaming = tw686x_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
static int tw686x_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct tw686x_video_channel *vc;
struct tw686x_dev *dev;
unsigned int ch;
vc = container_of(ctrl->handler, struct tw686x_video_channel,
ctrl_handler);
dev = vc->dev;
ch = vc->ch;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
reg_write(dev, BRIGHT[ch], ctrl->val & 0xff);
return 0;
case V4L2_CID_CONTRAST:
reg_write(dev, CONTRAST[ch], ctrl->val);
return 0;
case V4L2_CID_SATURATION:
reg_write(dev, SAT_U[ch], ctrl->val);
reg_write(dev, SAT_V[ch], ctrl->val);
return 0;
case V4L2_CID_HUE:
reg_write(dev, HUE[ch], ctrl->val & 0xff);
return 0;
}
return -EINVAL;
}
static const struct v4l2_ctrl_ops ctrl_ops = {
.s_ctrl = tw686x_s_ctrl,
};
static int tw686x_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct tw686x_video_channel *vc = video_drvdata(file);
f->fmt.pix.width = vc->width;
f->fmt.pix.height = vc->height;
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
f->fmt.pix.pixelformat = vc->format->fourcc;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.bytesperline = (f->fmt.pix.width * vc->format->depth) / 8;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
return 0;
}
static int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct tw686x_video_channel *vc = video_drvdata(file);
unsigned int video_height = TW686X_VIDEO_HEIGHT(vc->video_standard);
const struct tw686x_format *format;
format = format_by_fourcc(f->fmt.pix.pixelformat);
if (!format) {
format = &formats[0];
f->fmt.pix.pixelformat = format->fourcc;
}
if (f->fmt.pix.width <= TW686X_VIDEO_WIDTH / 2)
f->fmt.pix.width = TW686X_VIDEO_WIDTH / 2;
else
f->fmt.pix.width = TW686X_VIDEO_WIDTH;
if (f->fmt.pix.height <= video_height / 2)
f->fmt.pix.height = video_height / 2;
else
f->fmt.pix.height = video_height;
f->fmt.pix.bytesperline = (f->fmt.pix.width * format->depth) / 8;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
return 0;
}
static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct tw686x_video_channel *vc = video_drvdata(file);
u32 val, width, line_width, height;
unsigned long bitsperframe;
int err, pb;
if (vb2_is_busy(&vc->vidq))
return -EBUSY;
bitsperframe = vc->width * vc->height * vc->format->depth;
err = tw686x_try_fmt_vid_cap(file, priv, f);
if (err)
return err;
vc->format = format_by_fourcc(f->fmt.pix.pixelformat);
vc->width = f->fmt.pix.width;
vc->height = f->fmt.pix.height;
/* We need new DMA buffers if the framesize has changed */
if (bitsperframe != vc->width * vc->height * vc->format->depth) {
for (pb = 0; pb < 2; pb++)
tw686x_free_dma(vc, pb);
for (pb = 0; pb < 2; pb++) {
err = tw686x_alloc_dma(vc, pb);
if (err) {
if (pb > 0)
tw686x_free_dma(vc, 0);
return err;
}
}
}
val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]);
if (vc->width <= TW686X_VIDEO_WIDTH / 2)
val |= BIT(23);
else
val &= ~BIT(23);
if (vc->height <= TW686X_VIDEO_HEIGHT(vc->video_standard) / 2)
val |= BIT(24);
else
val &= ~BIT(24);
val &= ~(0x7 << 20);
val |= vc->format->mode << 20;
reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val);
/* Program the DMA frame size */
width = (vc->width * 2) & 0x7ff;
height = vc->height / 2;
line_width = (vc->width * 2) & 0x7ff;
val = (height << 22) | (line_width << 11) | width;
reg_write(vc->dev, VDMA_WHP[vc->ch], val);
return 0;
}
static int tw686x_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct tw686x_video_channel *vc = video_drvdata(file);
struct tw686x_dev *dev = vc->dev;
strlcpy(cap->driver, "tw686x", sizeof(cap->driver));
strlcpy(cap->card, dev->name, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info),
"PCI:%s", pci_name(dev->pci_dev));
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
{
struct tw686x_video_channel *vc = video_drvdata(file);
struct v4l2_format f;
u32 val, ret;
if (vc->video_standard == id)
return 0;
if (vb2_is_busy(&vc->vidq))
return -EBUSY;
if (id & V4L2_STD_NTSC)
val = 0;
else if (id & V4L2_STD_PAL)
val = 1;
else if (id & V4L2_STD_SECAM)
val = 2;
else if (id & V4L2_STD_NTSC_443)
val = 3;
else if (id & V4L2_STD_PAL_M)
val = 4;
else if (id & V4L2_STD_PAL_Nc)
val = 5;
else if (id & V4L2_STD_PAL_60)
val = 6;
else
return -EINVAL;
vc->video_standard = id;
reg_write(vc->dev, SDT[vc->ch], val);
val = reg_read(vc->dev, VIDEO_CONTROL1);
if (id & V4L2_STD_525_60)
val &= ~(1 << (SYS_MODE_DMA_SHIFT + vc->ch));
else
val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch));
reg_write(vc->dev, VIDEO_CONTROL1, val);
/*
* Adjust format after V4L2_STD_525_60/V4L2_STD_625_50 change,
* calling g_fmt and s_fmt will sanitize the height
* according to the standard.
*/
ret = tw686x_g_fmt_vid_cap(file, priv, &f);
if (!ret)
tw686x_s_fmt_vid_cap(file, priv, &f);
return 0;
}
static int tw686x_querystd(struct file *file, void *priv, v4l2_std_id *std)
{
struct tw686x_video_channel *vc = video_drvdata(file);
struct tw686x_dev *dev = vc->dev;
unsigned int old_std, detected_std = 0;
unsigned long end;
if (vb2_is_streaming(&vc->vidq))
return -EBUSY;
/* Enable and start standard detection */
old_std = reg_read(dev, SDT[vc->ch]);
reg_write(dev, SDT[vc->ch], 0x7);
reg_write(dev, SDT_EN[vc->ch], 0xff);
end = jiffies + msecs_to_jiffies(500);
while (time_is_after_jiffies(end)) {
detected_std = reg_read(dev, SDT[vc->ch]);
if (!(detected_std & BIT(7)))
break;
msleep(100);
}
reg_write(dev, SDT[vc->ch], old_std);
/* Exit if still busy */
if (detected_std & BIT(7))
return 0;
detected_std = (detected_std >> 4) & 0x7;
switch (detected_std) {
case TW686X_STD_NTSC_M:
*std &= V4L2_STD_NTSC;
break;
case TW686X_STD_NTSC_443:
*std &= V4L2_STD_NTSC_443;
break;
case TW686X_STD_PAL_M:
*std &= V4L2_STD_PAL_M;
break;
case TW686X_STD_PAL_60:
*std &= V4L2_STD_PAL_60;
break;
case TW686X_STD_PAL:
*std &= V4L2_STD_PAL;
break;
case TW686X_STD_PAL_CN:
*std &= V4L2_STD_PAL_Nc;
break;
case TW686X_STD_SECAM:
*std &= V4L2_STD_SECAM;
break;
default:
*std = 0;
}
return 0;
}
static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id)
{
struct tw686x_video_channel *vc = video_drvdata(file);
*id = vc->video_standard;
return 0;
}
static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index >= ARRAY_SIZE(formats))
return -EINVAL;
f->pixelformat = formats[f->index].fourcc;
return 0;
}
static int tw686x_s_input(struct file *file, void *priv, unsigned int i)
{
struct tw686x_video_channel *vc = video_drvdata(file);
u32 val;
if (i >= TW686X_INPUTS_PER_CH)
return -EINVAL;
if (i == vc->input)
return 0;
/*
* Not sure we are able to support on the fly input change
*/
if (vb2_is_busy(&vc->vidq))
return -EBUSY;
vc->input = i;
val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]);
val &= ~(0x3 << 30);
val |= i << 30;
reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val);
return 0;
}
static int tw686x_g_input(struct file *file, void *priv, unsigned int *i)
{
struct tw686x_video_channel *vc = video_drvdata(file);
*i = vc->input;
return 0;
}
static int tw686x_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
struct tw686x_video_channel *vc = video_drvdata(file);
unsigned int vidstat;
if (i->index >= TW686X_INPUTS_PER_CH)
return -EINVAL;
snprintf(i->name, sizeof(i->name), "Composite%d", i->index);
i->type = V4L2_INPUT_TYPE_CAMERA;
i->std = vc->device->tvnorms;
i->capabilities = V4L2_IN_CAP_STD;
vidstat = reg_read(vc->dev, VIDSTAT[vc->ch]);
i->status = 0;
if (vidstat & TW686X_VIDSTAT_VDLOSS)
i->status |= V4L2_IN_ST_NO_SIGNAL;
if (!(vidstat & TW686X_VIDSTAT_HLOCK))
i->status |= V4L2_IN_ST_NO_H_LOCK;
return 0;
}
static const struct v4l2_file_operations tw686x_video_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.unlocked_ioctl = video_ioctl2,
.release = vb2_fop_release,
.poll = vb2_fop_poll,
.read = vb2_fop_read,
.mmap = vb2_fop_mmap,
};
static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = {
.vidioc_querycap = tw686x_querycap,
.vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap,
.vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = tw686x_try_fmt_vid_cap,
.vidioc_querystd = tw686x_querystd,
.vidioc_g_std = tw686x_g_std,
.vidioc_s_std = tw686x_s_std,
.vidioc_enum_input = tw686x_enum_input,
.vidioc_g_input = tw686x_g_input,
.vidioc_s_input = tw686x_s_input,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static void tw686x_buffer_copy(struct tw686x_video_channel *vc,
unsigned int pb, struct vb2_v4l2_buffer *vb)
{
struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
struct vb2_buffer *vb2_buf = &vb->vb2_buf;
vb->field = V4L2_FIELD_INTERLACED;
vb->sequence = vc->sequence++;
memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt, desc->size);
vb2_buf->timestamp = ktime_get_ns();
vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE);
}
void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests,
unsigned int pb_status, unsigned int fifo_status,
unsigned int *reset_ch)
{
struct tw686x_video_channel *vc;
struct vb2_v4l2_buffer *vb;
unsigned long flags;
unsigned int ch, pb;
for_each_set_bit(ch, &requests, max_channels(dev)) {
vc = &dev->video_channels[ch];
/*
* This can either be a blue frame (with signal-lost bit set)
* or a good frame (with signal-lost bit clear). If we have just
* got signal, then this channel needs resetting.
*/
if (vc->no_signal && !(fifo_status & BIT(ch))) {
v4l2_printk(KERN_DEBUG, &dev->v4l2_dev,
"video%d: signal recovered\n", vc->num);
vc->no_signal = false;
*reset_ch |= BIT(ch);
vc->pb = 0;
continue;
}
vc->no_signal = !!(fifo_status & BIT(ch));
/* Check FIFO errors only if there's signal */
if (!vc->no_signal) {
u32 fifo_ov, fifo_bad;
fifo_ov = (fifo_status >> 24) & BIT(ch);
fifo_bad = (fifo_status >> 16) & BIT(ch);
if (fifo_ov || fifo_bad) {
/* Mark this channel for reset */
v4l2_printk(KERN_DEBUG, &dev->v4l2_dev,
"video%d: FIFO error\n", vc->num);
*reset_ch |= BIT(ch);
vc->pb = 0;
continue;
}
}
pb = !!(pb_status & BIT(ch));
if (vc->pb != pb) {
/* Mark this channel for reset */
v4l2_printk(KERN_DEBUG, &dev->v4l2_dev,
"video%d: unexpected p-b buffer!\n",
vc->num);
*reset_ch |= BIT(ch);
vc->pb = 0;
continue;
}
/* handle video stream */
spin_lock_irqsave(&vc->qlock, flags);
if (vc->curr_bufs[pb]) {
vb = &vc->curr_bufs[pb]->vb;
tw686x_buffer_copy(vc, pb, vb);
}
vc->pb = !pb;
tw686x_buffer_refill(vc, pb);
spin_unlock_irqrestore(&vc->qlock, flags);
}
}
void tw686x_video_free(struct tw686x_dev *dev)
{
unsigned int ch, pb;
for (ch = 0; ch < max_channels(dev); ch++) {
struct tw686x_video_channel *vc = &dev->video_channels[ch];
if (vc->device)
video_unregister_device(vc->device);
for (pb = 0; pb < 2; pb++)
tw686x_free_dma(vc, pb);
}
}
int tw686x_video_init(struct tw686x_dev *dev)
{
unsigned int ch, val, pb;
int err;
err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev);
if (err)
return err;
for (ch = 0; ch < max_channels(dev); ch++) {
struct tw686x_video_channel *vc = &dev->video_channels[ch];
struct video_device *vdev;
mutex_init(&vc->vb_mutex);
spin_lock_init(&vc->qlock);
INIT_LIST_HEAD(&vc->vidq_queued);
vc->dev = dev;
vc->ch = ch;
/* default settings */
vc->format = &formats[0];
vc->video_standard = V4L2_STD_NTSC;
vc->width = TW686X_VIDEO_WIDTH;
vc->height = TW686X_VIDEO_HEIGHT(vc->video_standard);
vc->input = 0;
reg_write(vc->dev, SDT[ch], 0);
tw686x_set_framerate(vc, 30);
reg_write(dev, VDELAY_LO[ch], 0x14);
reg_write(dev, HACTIVE_LO[ch], 0xd0);
reg_write(dev, VIDEO_SIZE[ch], 0);
for (pb = 0; pb < 2; pb++) {
err = tw686x_alloc_dma(vc, pb);
if (err)
goto error;
}
vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vc->vidq.drv_priv = vc;
vc->vidq.buf_struct_size = sizeof(struct tw686x_v4l2_buf);
vc->vidq.ops = &tw686x_video_qops;
vc->vidq.mem_ops = &vb2_vmalloc_memops;
vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
vc->vidq.min_buffers_needed = 2;
vc->vidq.lock = &vc->vb_mutex;
vc->vidq.gfp_flags = GFP_DMA32;
err = vb2_queue_init(&vc->vidq);
if (err) {
v4l2_err(&dev->v4l2_dev,
"dma%d: cannot init vb2 queue\n", ch);
goto error;
}
err = v4l2_ctrl_handler_init(&vc->ctrl_handler, 4);
if (err) {
v4l2_err(&dev->v4l2_dev,
"dma%d: cannot init ctrl handler\n", ch);
goto error;
}
v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 100);
v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
V4L2_CID_SATURATION, 0, 255, 1, 128);
v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
err = vc->ctrl_handler.error;
if (err)
goto error;
err = v4l2_ctrl_handler_setup(&vc->ctrl_handler);
if (err)
goto error;
vdev = video_device_alloc();
if (!vdev) {
v4l2_err(&dev->v4l2_dev,
"dma%d: unable to allocate device\n", ch);
err = -ENOMEM;
goto error;
}
snprintf(vdev->name, sizeof(vdev->name), "%s video", dev->name);
vdev->fops = &tw686x_video_fops;
vdev->ioctl_ops = &tw686x_video_ioctl_ops;
vdev->release = video_device_release;
vdev->v4l2_dev = &dev->v4l2_dev;
vdev->queue = &vc->vidq;
vdev->tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50;
vdev->minor = -1;
vdev->lock = &vc->vb_mutex;
vdev->ctrl_handler = &vc->ctrl_handler;
vc->device = vdev;
video_set_drvdata(vdev, vc);
err = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (err < 0)
goto error;
vc->num = vdev->num;
}
/* Set DMA frame mode on all channels. Only supported mode for now. */
val = TW686X_DEF_PHASE_REF;
for (ch = 0; ch < max_channels(dev); ch++)
val |= TW686X_FRAME_MODE << (16 + ch * 2);
reg_write(dev, PHASE_REF, val);
reg_write(dev, MISC2[0], 0xe7);
reg_write(dev, VCTRL1[0], 0xcc);
reg_write(dev, LOOP[0], 0xa5);
if (max_channels(dev) > 4) {
reg_write(dev, VCTRL1[1], 0xcc);
reg_write(dev, LOOP[1], 0xa5);
reg_write(dev, MISC2[1], 0xe7);
}
return 0;
error:
tw686x_video_free(dev);
return err;
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar
*
* Copyright (C) 2015 Industrial Research Institute for Automation
* and Measurements PIAP
* Written by Krzysztof Ha?asa
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*/
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/timer.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include <sound/pcm.h>
#include "tw686x-regs.h"
#define TYPE_MAX_CHANNELS 0x0f
#define TYPE_SECOND_GEN 0x10
#define TW686X_DEF_PHASE_REF 0x1518
#define TW686X_FIELD_MODE 0x3
#define TW686X_FRAME_MODE 0x2
/* 0x1 is reserved */
#define TW686X_SG_MODE 0x0
#define TW686X_AUDIO_PAGE_SZ 4096
#define TW686X_AUDIO_PAGE_MAX 16
#define TW686X_AUDIO_PERIODS_MIN 2
#define TW686X_AUDIO_PERIODS_MAX TW686X_AUDIO_PAGE_MAX
struct tw686x_format {
char *name;
unsigned int fourcc;
unsigned int depth;
unsigned int mode;
};
struct tw686x_dma_desc {
dma_addr_t phys;
void *virt;
unsigned int size;
};
struct tw686x_audio_buf {
dma_addr_t dma;
void *virt;
struct list_head list;
};
struct tw686x_v4l2_buf {
struct vb2_v4l2_buffer vb;
struct list_head list;
};
struct tw686x_audio_channel {
struct tw686x_dev *dev;
struct snd_pcm_substream *ss;
unsigned int ch;
struct tw686x_audio_buf *curr_bufs[2];
struct tw686x_dma_desc dma_descs[2];
dma_addr_t ptr;
struct tw686x_audio_buf buf[TW686X_AUDIO_PAGE_MAX];
struct list_head buf_list;
spinlock_t lock;
};
struct tw686x_video_channel {
struct tw686x_dev *dev;
struct vb2_queue vidq;
struct list_head vidq_queued;
struct video_device *device;
struct tw686x_v4l2_buf *curr_bufs[2];
struct tw686x_dma_desc dma_descs[2];
struct v4l2_ctrl_handler ctrl_handler;
const struct tw686x_format *format;
struct mutex vb_mutex;
spinlock_t qlock;
v4l2_std_id video_standard;
unsigned int width, height;
unsigned int h_halve, v_halve;
unsigned int ch;
unsigned int num;
unsigned int fps;
unsigned int input;
unsigned int sequence;
unsigned int pb;
bool no_signal;
};
/**
* struct tw686x_dev - global device status
* @lock: spinlock controlling access to the
* shared device registers (DMA enable/disable).
*/
struct tw686x_dev {
spinlock_t lock;
struct v4l2_device v4l2_dev;
struct snd_card *snd_card;
char name[32];
unsigned int type;
struct pci_dev *pci_dev;
__u32 __iomem *mmio;
void *alloc_ctx;
struct tw686x_video_channel *video_channels;
struct tw686x_audio_channel *audio_channels;
int audio_rate; /* per-device value */
struct timer_list dma_delay_timer;
u32 pending_dma_en; /* must be protected by lock */
u32 pending_dma_cmd; /* must be protected by lock */
};
static inline uint32_t reg_read(struct tw686x_dev *dev, unsigned int reg)
{
return readl(dev->mmio + reg);
}
static inline void reg_write(struct tw686x_dev *dev, unsigned int reg,
uint32_t value)
{
writel(value, dev->mmio + reg);
}
static inline unsigned int max_channels(struct tw686x_dev *dev)
{
return dev->type & TYPE_MAX_CHANNELS; /* 4 or 8 channels */
}
void tw686x_enable_channel(struct tw686x_dev *dev, unsigned int channel);
void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel);
int tw686x_video_init(struct tw686x_dev *dev);
void tw686x_video_free(struct tw686x_dev *dev);
void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests,
unsigned int pb_status, unsigned int fifo_status,
unsigned int *reset_ch);
int tw686x_audio_init(struct tw686x_dev *dev);
void tw686x_audio_free(struct tw686x_dev *dev);
void tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests,
unsigned int pb_status);

View file

@ -116,8 +116,9 @@ videocodec_attach (struct videocodec_master *master)
goto out_module_put;
}
snprintf(codec->name, sizeof(codec->name),
"%s[%d]", codec->name, h->attached);
res = strlen(codec->name);
snprintf(codec->name + res, sizeof(codec->name) - res,
"[%d]", h->attached);
codec->master_data = master;
res = codec->setup(codec);
if (res == 0) {

View file

@ -238,7 +238,7 @@ config VIDEO_SH_VEU
config VIDEO_RENESAS_JPU
tristate "Renesas JPEG Processing Unit"
depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
depends on ARCH_SHMOBILE || COMPILE_TEST
depends on ARCH_RENESAS || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
---help---
@ -250,7 +250,7 @@ config VIDEO_RENESAS_JPU
config VIDEO_RENESAS_VSP1
tristate "Renesas VSP1 Video Processing Engine"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
depends on (ARCH_SHMOBILE && OF) || COMPILE_TEST
depends on (ARCH_RENESAS && OF) || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
---help---
This is a V4L2 driver for the Renesas VSP1 video processing engine.

View file

@ -1047,7 +1047,7 @@ static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe,
static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe)
{
enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED;
int ret;
int ret = 0;
vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n");
@ -1706,7 +1706,7 @@ static int vpfe_get_app_input_index(struct vpfe_device *vpfe,
sdinfo = &cfg->sub_devs[i];
client = v4l2_get_subdevdata(sdinfo->sd);
if (client->addr == curr_client->addr &&
client->adapter->nr == client->adapter->nr) {
client->adapter->nr == curr_client->adapter->nr) {
if (vpfe->current_input >= 1)
return -1;
*app_input_index = j + vpfe->current_input;

View file

@ -967,15 +967,6 @@ static struct gsc_driverdata gsc_v_100_drvdata = {
.lclk_frequency = 266000000UL,
};
static const struct platform_device_id gsc_driver_ids[] = {
{
.name = "exynos-gsc",
.driver_data = (unsigned long)&gsc_v_100_drvdata,
},
{},
};
MODULE_DEVICE_TABLE(platform, gsc_driver_ids);
static const struct of_device_id exynos_gsc_match[] = {
{
.compatible = "samsung,exynos5-gsc",
@ -988,17 +979,11 @@ MODULE_DEVICE_TABLE(of, exynos_gsc_match);
static void *gsc_get_drv_data(struct platform_device *pdev)
{
struct gsc_driverdata *driver_data = NULL;
const struct of_device_id *match;
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(exynos_gsc_match,
pdev->dev.of_node);
if (match)
driver_data = (struct gsc_driverdata *)match->data;
} else {
driver_data = (struct gsc_driverdata *)
platform_get_device_id(pdev)->driver_data;
}
match = of_match_node(exynos_gsc_match, pdev->dev.of_node);
if (match)
driver_data = (struct gsc_driverdata *)match->data;
return driver_data;
}
@ -1078,17 +1063,17 @@ static int gsc_probe(struct platform_device *pdev)
struct resource *res;
struct gsc_driverdata *drv_data = gsc_get_drv_data(pdev);
struct device *dev = &pdev->dev;
int ret = 0;
int ret;
gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL);
if (!gsc)
return -ENOMEM;
if (dev->of_node)
gsc->id = of_alias_get_id(pdev->dev.of_node, "gsc");
else
gsc->id = pdev->id;
ret = of_alias_get_id(pdev->dev.of_node, "gsc");
if (ret < 0)
return ret;
gsc->id = ret;
if (gsc->id >= drv_data->num_entities) {
dev_err(dev, "Invalid platform device id: %d\n", gsc->id);
return -EINVAL;
@ -1096,7 +1081,6 @@ static int gsc_probe(struct platform_device *pdev)
gsc->variant = drv_data->variant[gsc->id];
gsc->pdev = pdev;
gsc->pdata = dev->platform_data;
init_waitqueue_head(&gsc->irq_queue);
spin_lock_init(&gsc->slock);
@ -1253,7 +1237,6 @@ static const struct dev_pm_ops gsc_pm_ops = {
static struct platform_driver gsc_driver = {
.probe = gsc_probe,
.remove = gsc_remove,
.id_table = gsc_driver_ids,
.driver = {
.name = GSC_MODULE_NAME,
.pm = &gsc_pm_ops,

View file

@ -340,7 +340,6 @@ struct gsc_dev {
void __iomem *regs;
wait_queue_head_t irq_queue;
struct gsc_m2m_device m2m;
struct exynos_platform_gscaler *pdata;
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
struct video_device vdev;

View file

@ -1154,26 +1154,6 @@ static const struct fimc_pix_limit s5p_pix_limit[4] = {
},
};
static const struct fimc_variant fimc0_variant_s5p = {
.has_inp_rot = 1,
.has_out_rot = 1,
.has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
.min_vsize_align = 16,
.pix_limit = &s5p_pix_limit[0],
};
static const struct fimc_variant fimc2_variant_s5p = {
.has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
.min_vsize_align = 16,
.pix_limit = &s5p_pix_limit[1],
};
static const struct fimc_variant fimc0_variant_s5pv210 = {
.has_inp_rot = 1,
.has_out_rot = 1,
@ -1206,18 +1186,6 @@ static const struct fimc_variant fimc2_variant_s5pv210 = {
.pix_limit = &s5p_pix_limit[2],
};
/* S5PC100 */
static const struct fimc_drvdata fimc_drvdata_s5p = {
.variant = {
[0] = &fimc0_variant_s5p,
[1] = &fimc0_variant_s5p,
[2] = &fimc2_variant_s5p,
},
.num_entities = 3,
.lclk_frequency = 133000000UL,
.out_buf_count = 4,
};
/* S5PV210, S5PC110 */
static const struct fimc_drvdata fimc_drvdata_s5pv210 = {
.variant = {
@ -1251,23 +1219,6 @@ static const struct fimc_drvdata fimc_drvdata_exynos4x12 = {
.out_buf_count = 32,
};
static const struct platform_device_id fimc_driver_ids[] = {
{
.name = "s5p-fimc",
.driver_data = (unsigned long)&fimc_drvdata_s5p,
}, {
.name = "s5pv210-fimc",
.driver_data = (unsigned long)&fimc_drvdata_s5pv210,
}, {
.name = "exynos4-fimc",
.driver_data = (unsigned long)&fimc_drvdata_exynos4210,
}, {
.name = "exynos4x12-fimc",
.driver_data = (unsigned long)&fimc_drvdata_exynos4x12,
},
{ },
};
static const struct of_device_id fimc_of_match[] = {
{
.compatible = "samsung,s5pv210-fimc",
@ -1290,7 +1241,6 @@ static const struct dev_pm_ops fimc_pm_ops = {
static struct platform_driver fimc_driver = {
.probe = fimc_probe,
.remove = fimc_remove,
.id_table = fimc_driver_ids,
.driver = {
.of_match_table = fimc_of_match,
.name = FIMC_DRIVER_NAME,

View file

@ -446,8 +446,10 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
else
pd->fimc_bus_type = pd->sensor_bus_type;
if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) {
of_node_put(rem);
return -EINVAL;
}
fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF;
fmd->sensor[index].asd.match.of.node = rem;
@ -1130,7 +1132,7 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable,
media_entity_graph_walk_start(graph, entity);
while ((entity = media_entity_graph_walk_next(graph))) {
if (!is_media_entity_v4l2_io(entity))
if (!is_media_entity_v4l2_video_device(entity))
continue;
ret = __fimc_md_modify_pipeline(entity, enable);
@ -1145,7 +1147,7 @@ err:
media_entity_graph_walk_start(graph, entity_err);
while ((entity_err = media_entity_graph_walk_next(graph))) {
if (!is_media_entity_v4l2_io(entity_err))
if (!is_media_entity_v4l2_video_device(entity_err))
continue;
__fimc_md_modify_pipeline(entity_err, !enable);

View file

@ -757,8 +757,10 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
goto err;
state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;
if (state->index >= CSIS_MAX_ENTITIES)
return -ENXIO;
if (state->index >= CSIS_MAX_ENTITIES) {
ret = -ENXIO;
goto err;
}
/* Get MIPI CSI-2 bus configration from the endpoint node. */
of_property_read_u32(node, "samsung,csis-hs-settle",

View file

@ -251,7 +251,7 @@ static int isp_video_get_graph_data(struct isp_video *video,
if (entity == &video->video.entity)
continue;
if (!is_media_entity_v4l2_io(entity))
if (!is_media_entity_v4l2_video_device(entity))
continue;
__video = to_isp_video(media_entity_to_video_device(entity));

View file

@ -719,16 +719,12 @@ static int g2d_probe(struct platform_device *pdev)
def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
if (!pdev->dev.of_node) {
dev->variant = g2d_get_drv_data(pdev);
} else {
of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node);
if (!of_id) {
ret = -ENODEV;
goto unreg_video_dev;
}
dev->variant = (struct g2d_variant *)of_id->data;
of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node);
if (!of_id) {
ret = -ENODEV;
goto unreg_video_dev;
}
dev->variant = (struct g2d_variant *)of_id->data;
return 0;
@ -788,22 +784,9 @@ static const struct of_device_id exynos_g2d_match[] = {
};
MODULE_DEVICE_TABLE(of, exynos_g2d_match);
static const struct platform_device_id g2d_driver_ids[] = {
{
.name = "s5p-g2d",
.driver_data = (unsigned long)&g2d_drvdata_v3x,
}, {
.name = "s5p-g2d-v4x",
.driver_data = (unsigned long)&g2d_drvdata_v4x,
},
{},
};
MODULE_DEVICE_TABLE(platform, g2d_driver_ids);
static struct platform_driver g2d_pdrv = {
.probe = g2d_probe,
.remove = g2d_remove,
.id_table = g2d_driver_ids,
.driver = {
.name = G2D_NAME,
.of_match_table = exynos_g2d_match,

View file

@ -89,8 +89,3 @@ void g2d_set_flip(struct g2d_dev *d, u32 r);
void g2d_set_v41_stretch(struct g2d_dev *d,
struct g2d_frame *src, struct g2d_frame *dst);
void g2d_set_cmd(struct g2d_dev *d, u32 c);
static inline struct g2d_variant *g2d_get_drv_data(struct platform_device *pdev)
{
return (struct g2d_variant *)platform_get_device_id(pdev)->driver_data;
}

View file

@ -1548,8 +1548,10 @@ static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx,
struct v4l2_pix_format *pix = &f->fmt.pix;
u32 pix_fmt = f->fmt.pix.pixelformat;
int w = pix->width, h = pix->height, wh_align;
int padding = 0;
if (pix_fmt == V4L2_PIX_FMT_RGB32 ||
pix_fmt == V4L2_PIX_FMT_RGB565 ||
pix_fmt == V4L2_PIX_FMT_NV24 ||
pix_fmt == V4L2_PIX_FMT_NV42 ||
pix_fmt == V4L2_PIX_FMT_NV12 ||
@ -1564,7 +1566,10 @@ static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx,
&h, S5P_JPEG_MIN_HEIGHT,
S5P_JPEG_MAX_HEIGHT, wh_align);
return w * h * fmt_depth >> 3;
if (ctx->jpeg->variant->version == SJPEG_EXYNOS4)
padding = PAGE_SIZE;
return (w * h * fmt_depth >> 3) + padding;
}
static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx,

View file

@ -1489,27 +1489,6 @@ static struct s5p_mfc_variant mfc_drvdata_v8 = {
.fw_name[0] = "s5p-mfc-v8.fw",
};
static const struct platform_device_id mfc_driver_ids[] = {
{
.name = "s5p-mfc",
.driver_data = (unsigned long)&mfc_drvdata_v5,
}, {
.name = "s5p-mfc-v5",
.driver_data = (unsigned long)&mfc_drvdata_v5,
}, {
.name = "s5p-mfc-v6",
.driver_data = (unsigned long)&mfc_drvdata_v6,
}, {
.name = "s5p-mfc-v7",
.driver_data = (unsigned long)&mfc_drvdata_v7,
}, {
.name = "s5p-mfc-v8",
.driver_data = (unsigned long)&mfc_drvdata_v8,
},
{},
};
MODULE_DEVICE_TABLE(platform, mfc_driver_ids);
static const struct of_device_id exynos_mfc_match[] = {
{
.compatible = "samsung,mfc-v5",
@ -1531,24 +1510,18 @@ MODULE_DEVICE_TABLE(of, exynos_mfc_match);
static void *mfc_get_drv_data(struct platform_device *pdev)
{
struct s5p_mfc_variant *driver_data = NULL;
const struct of_device_id *match;
match = of_match_node(exynos_mfc_match, pdev->dev.of_node);
if (match)
driver_data = (struct s5p_mfc_variant *)match->data;
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(exynos_mfc_match,
pdev->dev.of_node);
if (match)
driver_data = (struct s5p_mfc_variant *)match->data;
} else {
driver_data = (struct s5p_mfc_variant *)
platform_get_device_id(pdev)->driver_data;
}
return driver_data;
}
static struct platform_driver s5p_mfc_driver = {
.probe = s5p_mfc_probe,
.remove = s5p_mfc_remove,
.id_table = mfc_driver_ids,
.driver = {
.name = S5P_MFC_NAME,
.pm = &s5p_mfc_pm_ops,

View file

@ -300,7 +300,7 @@ void mxr_release_video(struct mxr_device *mdev);
struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx);
struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx);
struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
int idx, char *name, struct mxr_layer_ops *ops);
int idx, char *name, const struct mxr_layer_ops *ops);
void mxr_base_layer_release(struct mxr_layer *layer);
void mxr_layer_release(struct mxr_layer *layer);

View file

@ -235,7 +235,7 @@ struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx)
{
struct mxr_layer *layer;
int ret;
struct mxr_layer_ops ops = {
const struct mxr_layer_ops ops = {
.release = mxr_graph_layer_release,
.buffer_set = mxr_graph_buffer_set,
.stream_set = mxr_graph_stream_set,

View file

@ -1070,7 +1070,7 @@ static void mxr_vfd_release(struct video_device *vdev)
}
struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
int idx, char *name, struct mxr_layer_ops *ops)
int idx, char *name, const struct mxr_layer_ops *ops)
{
struct mxr_layer *layer;

View file

@ -207,7 +207,7 @@ struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx)
{
struct mxr_layer *layer;
int ret;
struct mxr_layer_ops ops = {
const struct mxr_layer_ops ops = {
.release = mxr_vp_layer_release,
.buffer_set = mxr_vp_buffer_set,
.stream_set = mxr_vp_stream_set,

View file

@ -28,7 +28,7 @@ config VIDEO_PXA27x
config VIDEO_RCAR_VIN
tristate "R-Car Video Input (VIN) support"
depends on VIDEO_DEV && SOC_CAMERA
depends on ARCH_SHMOBILE || COMPILE_TEST
depends on ARCH_RENESAS || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select SOC_CAMERA_SCALE_CROP
@ -45,7 +45,7 @@ config VIDEO_SH_MOBILE_CSI2
config VIDEO_SH_MOBILE_CEU
tristate "SuperH Mobile CEU Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST
depends on ARCH_SHMOBILE || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select SOC_CAMERA_SCALE_CROP

View file

@ -1845,6 +1845,8 @@ static const struct of_device_id rcar_vin_of_table[] = {
{ .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 },
{ .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 },
{ .compatible = "renesas,rcar-gen3-vin", .data = (void *)RCAR_GEN3 },
{ .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 },
{ },
};
MODULE_DEVICE_TABLE(of, rcar_vin_of_table);

View file

@ -49,7 +49,7 @@ MODULE_FIRMWARE(FIRMWARE_MEMDMA);
#define PID_TABLE_SIZE 1024
#define POLL_MSECS 50
static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei);
static int load_c8sectpfe_fw(struct c8sectpfei *fei);
#define TS_PKT_SIZE 188
#define HEADER_SIZE (4)
@ -130,7 +130,7 @@ static void channel_swdemux_tsklet(unsigned long data)
writel(channel->back_buffer_busaddr, channel->irec +
DMA_PRDS_BUSRP_TP(0));
else
writel(wp, channel->irec + DMA_PRDS_BUSWP_TP(0));
writel(wp, channel->irec + DMA_PRDS_BUSRP_TP(0));
}
static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
@ -141,6 +141,7 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
struct channel_info *channel;
u32 tmp;
unsigned long *bitmap;
int ret;
switch (dvbdmxfeed->type) {
case DMX_TYPE_TS:
@ -169,8 +170,9 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
}
if (!atomic_read(&fei->fw_loaded)) {
dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__);
return -EINVAL;
ret = load_c8sectpfe_fw(fei);
if (ret)
return ret;
}
mutex_lock(&fei->lock);
@ -265,8 +267,9 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
unsigned long *bitmap;
if (!atomic_read(&fei->fw_loaded)) {
dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__);
return -EINVAL;
ret = load_c8sectpfe_fw(fei);
if (ret)
return ret;
}
mutex_lock(&fei->lock);
@ -585,7 +588,7 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei,
writel(tsin->pid_buffer_busaddr,
fei->io + PIDF_BASE(tsin->tsin_id));
dev_info(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n",
dev_dbg(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n",
tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)),
&tsin->pid_buffer_busaddr);
@ -880,13 +883,6 @@ static int c8sectpfe_probe(struct platform_device *pdev)
goto err_clk_disable;
}
/* ensure all other init has been done before requesting firmware */
ret = load_c8sectpfe_fw_step1(fei);
if (ret) {
dev_err(dev, "Couldn't load slim core firmware\n");
goto err_clk_disable;
}
c8sectpfe_debugfs_init(fei);
return 0;
@ -1091,15 +1087,14 @@ static void load_dmem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr,
phdr->p_memsz - phdr->p_filesz);
}
static int load_slim_core_fw(const struct firmware *fw, void *context)
static int load_slim_core_fw(const struct firmware *fw, struct c8sectpfei *fei)
{
struct c8sectpfei *fei = context;
Elf32_Ehdr *ehdr;
Elf32_Phdr *phdr;
u8 __iomem *dst;
int err = 0, i;
if (!fw || !context)
if (!fw || !fei)
return -EINVAL;
ehdr = (Elf32_Ehdr *)fw->data;
@ -1151,29 +1146,35 @@ static int load_slim_core_fw(const struct firmware *fw, void *context)
return err;
}
static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context)
static int load_c8sectpfe_fw(struct c8sectpfei *fei)
{
struct c8sectpfei *fei = context;
const struct firmware *fw;
int err;
dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA);
err = request_firmware(&fw, FIRMWARE_MEMDMA, fei->dev);
if (err)
return err;
err = c8sectpfe_elf_sanity_check(fei, fw);
if (err) {
dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n"
, err);
goto err;
return err;
}
err = load_slim_core_fw(fw, context);
err = load_slim_core_fw(fw, fei);
if (err) {
dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err);
goto err;
return err;
}
/* now the firmware is loaded configure the input blocks */
err = configure_channels(fei);
if (err) {
dev_err(fei->dev, "configure_channels failed err=(%d)\n", err);
goto err;
return err;
}
/*
@ -1186,28 +1187,6 @@ static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context)
writel(0x1, fei->io + DMA_CPU_RUN);
atomic_set(&fei->fw_loaded, 1);
err:
complete_all(&fei->fw_ack);
}
static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei)
{
int err;
dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA);
init_completion(&fei->fw_ack);
atomic_set(&fei->fw_loaded, 0);
err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
FIRMWARE_MEMDMA, fei->dev, GFP_KERNEL, fei,
load_c8sectpfe_fw_cb);
if (err) {
dev_err(fei->dev, "request_firmware_nowait err: %d.\n", err);
complete_all(&fei->fw_ack);
return err;
}
return 0;
}

View file

@ -7,6 +7,7 @@ config VIDEO_VIVID
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select VIDEOBUF2_VMALLOC
select VIDEO_V4L2_TPG
default n
---help---
Enables a virtual video driver. This driver emulates a webcam,

View file

@ -2,5 +2,5 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \
vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \
vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \
vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \
vivid-osd.o vivid-tpg.o vivid-tpg-colors.o
vivid-osd.o
obj-$(CONFIG_VIDEO_VIVID) += vivid.o

View file

@ -200,27 +200,12 @@ static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct vivid_dev *dev = video_drvdata(file);
struct video_device *vdev = video_devdata(file);
strcpy(cap->driver, "vivid");
strcpy(cap->card, "vivid");
snprintf(cap->bus_info, sizeof(cap->bus_info),
"platform:%s", dev->v4l2_dev.name);
if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_RX)
cap->device_caps = dev->vid_cap_caps;
if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_TX)
cap->device_caps = dev->vid_out_caps;
else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_RX)
cap->device_caps = dev->vbi_cap_caps;
else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_TX)
cap->device_caps = dev->vbi_out_caps;
else if (vdev->vfl_type == VFL_TYPE_SDR)
cap->device_caps = dev->sdr_cap_caps;
else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_RX)
cap->device_caps = dev->radio_rx_caps;
else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_TX)
cap->device_caps = dev->radio_tx_caps;
cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps |
dev->vbi_cap_caps | dev->vbi_out_caps |
dev->radio_rx_caps | dev->radio_tx_caps |
@ -1135,6 +1120,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name));
vfd->fops = &vivid_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->device_caps = dev->vid_cap_caps;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->queue = &dev->vb_vid_cap_q;
@ -1160,6 +1146,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
vfd->vfl_dir = VFL_DIR_TX;
vfd->fops = &vivid_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->device_caps = dev->vid_out_caps;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->queue = &dev->vb_vid_out_q;
@ -1184,6 +1171,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name));
vfd->fops = &vivid_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->device_caps = dev->vbi_cap_caps;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->queue = &dev->vb_vbi_cap_q;
@ -1207,6 +1195,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
vfd->vfl_dir = VFL_DIR_TX;
vfd->fops = &vivid_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->device_caps = dev->vbi_out_caps;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->queue = &dev->vb_vbi_out_q;
@ -1229,6 +1218,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name));
vfd->fops = &vivid_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->device_caps = dev->sdr_cap_caps;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->queue = &dev->vb_sdr_cap_q;
@ -1247,6 +1237,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name));
vfd->fops = &vivid_radio_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->device_caps = dev->radio_rx_caps;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->lock = &dev->mutex;
@ -1265,6 +1256,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
vfd->vfl_dir = VFL_DIR_TX;
vfd->fops = &vivid_radio_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->device_caps = dev->radio_tx_caps;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->lock = &dev->mutex;

View file

@ -25,7 +25,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-ctrls.h>
#include "vivid-tpg.h"
#include <media/v4l2-tpg.h>
#include "vivid-rds-gen.h"
#include "vivid-vbi-gen.h"

View file

@ -36,6 +36,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-rect.h>
#include "vivid-core.h"
#include "vivid-vid-common.h"
@ -184,15 +185,15 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev)
dev->compose_out.width, dev->compose_out.height
};
dev->loop_vid_copy = rect_intersect(&dev->crop_cap, &dev->compose_out);
v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &dev->compose_out);
dev->loop_vid_out = dev->loop_vid_copy;
rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out);
v4l2_rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out);
dev->loop_vid_out.left += dev->crop_out.left;
dev->loop_vid_out.top += dev->crop_out.top;
dev->loop_vid_cap = dev->loop_vid_copy;
rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap);
v4l2_rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap);
dprintk(dev, 1,
"loop_vid_copy: %dx%d@%dx%d loop_vid_out: %dx%d@%dx%d loop_vid_cap: %dx%d@%dx%d\n",
@ -203,13 +204,13 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev)
dev->loop_vid_cap.width, dev->loop_vid_cap.height,
dev->loop_vid_cap.left, dev->loop_vid_cap.top);
r_overlay = rect_intersect(&r_fb, &r_overlay);
v4l2_rect_intersect(&r_overlay, &r_fb, &r_overlay);
/* shift r_overlay to the same origin as compose_out */
r_overlay.left += dev->compose_out.left - dev->overlay_out_left;
r_overlay.top += dev->compose_out.top - dev->overlay_out_top;
dev->loop_vid_overlay = rect_intersect(&r_overlay, &dev->loop_vid_copy);
v4l2_rect_intersect(&dev->loop_vid_overlay, &r_overlay, &dev->loop_vid_copy);
dev->loop_fb_copy = dev->loop_vid_overlay;
/* shift dev->loop_fb_copy back again to the fb origin */
@ -217,7 +218,7 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev)
dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top;
dev->loop_vid_overlay_cap = dev->loop_vid_overlay;
rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap);
v4l2_rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap);
dprintk(dev, 1,
"loop_fb_copy: %dx%d@%dx%d loop_vid_overlay: %dx%d@%dx%d loop_vid_overlay_cap: %dx%d@%dx%d\n",

View file

@ -55,6 +55,7 @@ void vivid_rds_generate(struct vivid_rds_gen *rds)
{
struct v4l2_rds_data *data = rds->data;
unsigned grp;
unsigned idx;
struct tm tm;
unsigned date;
unsigned time;
@ -73,24 +74,26 @@ void vivid_rds_generate(struct vivid_rds_gen *rds)
case 0 ... 3:
case 22 ... 25:
case 44 ... 47: /* Group 0B */
idx = (grp % 22) % 4;
data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
data[1].lsb |= vivid_get_di(rds, grp % 22);
data[1].lsb |= vivid_get_di(rds, idx);
data[1].msb |= 1 << 3;
data[2].lsb = rds->picode & 0xff;
data[2].msb = rds->picode >> 8;
data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
data[3].lsb = rds->psname[2 * (grp % 22) + 1];
data[3].msb = rds->psname[2 * (grp % 22)];
data[3].lsb = rds->psname[2 * idx + 1];
data[3].msb = rds->psname[2 * idx];
break;
case 4 ... 19:
case 26 ... 41: /* Group 2A */
data[1].lsb |= (grp - 4) % 22;
idx = ((grp - 4) % 22) % 16;
data[1].lsb |= idx;
data[1].msb |= 4 << 3;
data[2].msb = rds->radiotext[4 * ((grp - 4) % 22)];
data[2].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 1];
data[2].msb = rds->radiotext[4 * idx];
data[2].lsb = rds->radiotext[4 * idx + 1];
data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
data[3].msb = rds->radiotext[4 * ((grp - 4) % 22) + 2];
data[3].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 3];
data[3].msb = rds->radiotext[4 * idx + 2];
data[3].lsb = rds->radiotext[4 * idx + 3];
break;
case 56:
/*

View file

@ -1,68 +0,0 @@
/*
* vivid-color.h - Color definitions for the test pattern generator
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* This program is free software; you may redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _VIVID_COLORS_H_
#define _VIVID_COLORS_H_
struct color {
unsigned char r, g, b;
};
struct color16 {
int r, g, b;
};
enum tpg_color {
TPG_COLOR_CSC_WHITE,
TPG_COLOR_CSC_YELLOW,
TPG_COLOR_CSC_CYAN,
TPG_COLOR_CSC_GREEN,
TPG_COLOR_CSC_MAGENTA,
TPG_COLOR_CSC_RED,
TPG_COLOR_CSC_BLUE,
TPG_COLOR_CSC_BLACK,
TPG_COLOR_75_YELLOW,
TPG_COLOR_75_CYAN,
TPG_COLOR_75_GREEN,
TPG_COLOR_75_MAGENTA,
TPG_COLOR_75_RED,
TPG_COLOR_75_BLUE,
TPG_COLOR_100_WHITE,
TPG_COLOR_100_YELLOW,
TPG_COLOR_100_CYAN,
TPG_COLOR_100_GREEN,
TPG_COLOR_100_MAGENTA,
TPG_COLOR_100_RED,
TPG_COLOR_100_BLUE,
TPG_COLOR_100_BLACK,
TPG_COLOR_TEXTFG,
TPG_COLOR_TEXTBG,
TPG_COLOR_RANDOM,
TPG_COLOR_RAMP,
TPG_COLOR_MAX = TPG_COLOR_RAMP + 256
};
extern const struct color tpg_colors[TPG_COLOR_MAX];
extern const unsigned short tpg_rec709_to_linear[255 * 16 + 1];
extern const unsigned short tpg_linear_to_rec709[255 * 16 + 1];
extern const struct color16 tpg_csc_colors[V4L2_COLORSPACE_DCI_P3 + 1]
[V4L2_XFER_FUNC_SMPTE2084 + 1]
[TPG_COLOR_CSC_BLACK + 1];
#endif

View file

@ -1,598 +0,0 @@
/*
* vivid-tpg.h - Test Pattern Generator
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* This program is free software; you may redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _VIVID_TPG_H_
#define _VIVID_TPG_H_
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/videodev2.h>
#include "vivid-tpg-colors.h"
enum tpg_pattern {
TPG_PAT_75_COLORBAR,
TPG_PAT_100_COLORBAR,
TPG_PAT_CSC_COLORBAR,
TPG_PAT_100_HCOLORBAR,
TPG_PAT_100_COLORSQUARES,
TPG_PAT_BLACK,
TPG_PAT_WHITE,
TPG_PAT_RED,
TPG_PAT_GREEN,
TPG_PAT_BLUE,
TPG_PAT_CHECKERS_16X16,
TPG_PAT_CHECKERS_2X2,
TPG_PAT_CHECKERS_1X1,
TPG_PAT_COLOR_CHECKERS_2X2,
TPG_PAT_COLOR_CHECKERS_1X1,
TPG_PAT_ALTERNATING_HLINES,
TPG_PAT_ALTERNATING_VLINES,
TPG_PAT_CROSS_1_PIXEL,
TPG_PAT_CROSS_2_PIXELS,
TPG_PAT_CROSS_10_PIXELS,
TPG_PAT_GRAY_RAMP,
/* Must be the last pattern */
TPG_PAT_NOISE,
};
extern const char * const tpg_pattern_strings[];
enum tpg_quality {
TPG_QUAL_COLOR,
TPG_QUAL_GRAY,
TPG_QUAL_NOISE
};
enum tpg_video_aspect {
TPG_VIDEO_ASPECT_IMAGE,
TPG_VIDEO_ASPECT_4X3,
TPG_VIDEO_ASPECT_14X9_CENTRE,
TPG_VIDEO_ASPECT_16X9_CENTRE,
TPG_VIDEO_ASPECT_16X9_ANAMORPHIC,
};
enum tpg_pixel_aspect {
TPG_PIXEL_ASPECT_SQUARE,
TPG_PIXEL_ASPECT_NTSC,
TPG_PIXEL_ASPECT_PAL,
};
enum tpg_move_mode {
TPG_MOVE_NEG_FAST,
TPG_MOVE_NEG,
TPG_MOVE_NEG_SLOW,
TPG_MOVE_NONE,
TPG_MOVE_POS_SLOW,
TPG_MOVE_POS,
TPG_MOVE_POS_FAST,
};
extern const char * const tpg_aspect_strings[];
#define TPG_MAX_PLANES 3
#define TPG_MAX_PAT_LINES 8
struct tpg_data {
/* Source frame size */
unsigned src_width, src_height;
/* Buffer height */
unsigned buf_height;
/* Scaled output frame size */
unsigned scaled_width;
u32 field;
bool field_alternate;
/* crop coordinates are frame-based */
struct v4l2_rect crop;
/* compose coordinates are format-based */
struct v4l2_rect compose;
/* border and square coordinates are frame-based */
struct v4l2_rect border;
struct v4l2_rect square;
/* Color-related fields */
enum tpg_quality qual;
unsigned qual_offset;
u8 alpha_component;
bool alpha_red_only;
u8 brightness;
u8 contrast;
u8 saturation;
s16 hue;
u32 fourcc;
bool is_yuv;
u32 colorspace;
u32 xfer_func;
u32 ycbcr_enc;
/*
* Stores the actual transfer function, i.e. will never be
* V4L2_XFER_FUNC_DEFAULT.
*/
u32 real_xfer_func;
/*
* Stores the actual Y'CbCr encoding, i.e. will never be
* V4L2_YCBCR_ENC_DEFAULT.
*/
u32 real_ycbcr_enc;
u32 quantization;
/*
* Stores the actual quantization, i.e. will never be
* V4L2_QUANTIZATION_DEFAULT.
*/
u32 real_quantization;
enum tpg_video_aspect vid_aspect;
enum tpg_pixel_aspect pix_aspect;
unsigned rgb_range;
unsigned real_rgb_range;
unsigned buffers;
unsigned planes;
bool interleaved;
u8 vdownsampling[TPG_MAX_PLANES];
u8 hdownsampling[TPG_MAX_PLANES];
/*
* horizontal positions must be ANDed with this value to enforce
* correct boundaries for packed YUYV values.
*/
unsigned hmask[TPG_MAX_PLANES];
/* Used to store the colors in native format, either RGB or YUV */
u8 colors[TPG_COLOR_MAX][3];
u8 textfg[TPG_MAX_PLANES][8], textbg[TPG_MAX_PLANES][8];
/* size in bytes for two pixels in each plane */
unsigned twopixelsize[TPG_MAX_PLANES];
unsigned bytesperline[TPG_MAX_PLANES];
/* Configuration */
enum tpg_pattern pattern;
bool hflip;
bool vflip;
unsigned perc_fill;
bool perc_fill_blank;
bool show_border;
bool show_square;
bool insert_sav;
bool insert_eav;
/* Test pattern movement */
enum tpg_move_mode mv_hor_mode;
int mv_hor_count;
int mv_hor_step;
enum tpg_move_mode mv_vert_mode;
int mv_vert_count;
int mv_vert_step;
bool recalc_colors;
bool recalc_lines;
bool recalc_square_border;
/* Used to store TPG_MAX_PAT_LINES lines, each with up to two planes */
unsigned max_line_width;
u8 *lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES];
u8 *downsampled_lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES];
u8 *random_line[TPG_MAX_PLANES];
u8 *contrast_line[TPG_MAX_PLANES];
u8 *black_line[TPG_MAX_PLANES];
};
void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h);
int tpg_alloc(struct tpg_data *tpg, unsigned max_w);
void tpg_free(struct tpg_data *tpg);
void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
u32 field);
void tpg_log_status(struct tpg_data *tpg);
void tpg_set_font(const u8 *f);
void tpg_gen_text(const struct tpg_data *tpg,
u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text);
void tpg_calc_text_basep(struct tpg_data *tpg,
u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf);
unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line);
void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std,
unsigned p, u8 *vbuf);
void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std,
unsigned p, u8 *vbuf);
bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc);
void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
const struct v4l2_rect *compose);
static inline void tpg_s_pattern(struct tpg_data *tpg, enum tpg_pattern pattern)
{
if (tpg->pattern == pattern)
return;
tpg->pattern = pattern;
tpg->recalc_colors = true;
}
static inline void tpg_s_quality(struct tpg_data *tpg,
enum tpg_quality qual, unsigned qual_offset)
{
if (tpg->qual == qual && tpg->qual_offset == qual_offset)
return;
tpg->qual = qual;
tpg->qual_offset = qual_offset;
tpg->recalc_colors = true;
}
static inline enum tpg_quality tpg_g_quality(const struct tpg_data *tpg)
{
return tpg->qual;
}
static inline void tpg_s_alpha_component(struct tpg_data *tpg,
u8 alpha_component)
{
if (tpg->alpha_component == alpha_component)
return;
tpg->alpha_component = alpha_component;
tpg->recalc_colors = true;
}
static inline void tpg_s_alpha_mode(struct tpg_data *tpg,
bool red_only)
{
if (tpg->alpha_red_only == red_only)
return;
tpg->alpha_red_only = red_only;
tpg->recalc_colors = true;
}
static inline void tpg_s_brightness(struct tpg_data *tpg,
u8 brightness)
{
if (tpg->brightness == brightness)
return;
tpg->brightness = brightness;
tpg->recalc_colors = true;
}
static inline void tpg_s_contrast(struct tpg_data *tpg,
u8 contrast)
{
if (tpg->contrast == contrast)
return;
tpg->contrast = contrast;
tpg->recalc_colors = true;
}
static inline void tpg_s_saturation(struct tpg_data *tpg,
u8 saturation)
{
if (tpg->saturation == saturation)
return;
tpg->saturation = saturation;
tpg->recalc_colors = true;
}
static inline void tpg_s_hue(struct tpg_data *tpg,
s16 hue)
{
if (tpg->hue == hue)
return;
tpg->hue = hue;
tpg->recalc_colors = true;
}
static inline void tpg_s_rgb_range(struct tpg_data *tpg,
unsigned rgb_range)
{
if (tpg->rgb_range == rgb_range)
return;
tpg->rgb_range = rgb_range;
tpg->recalc_colors = true;
}
static inline void tpg_s_real_rgb_range(struct tpg_data *tpg,
unsigned rgb_range)
{
if (tpg->real_rgb_range == rgb_range)
return;
tpg->real_rgb_range = rgb_range;
tpg->recalc_colors = true;
}
static inline void tpg_s_colorspace(struct tpg_data *tpg, u32 colorspace)
{
if (tpg->colorspace == colorspace)
return;
tpg->colorspace = colorspace;
tpg->recalc_colors = true;
}
static inline u32 tpg_g_colorspace(const struct tpg_data *tpg)
{
return tpg->colorspace;
}
static inline void tpg_s_ycbcr_enc(struct tpg_data *tpg, u32 ycbcr_enc)
{
if (tpg->ycbcr_enc == ycbcr_enc)
return;
tpg->ycbcr_enc = ycbcr_enc;
tpg->recalc_colors = true;
}
static inline u32 tpg_g_ycbcr_enc(const struct tpg_data *tpg)
{
return tpg->ycbcr_enc;
}
static inline void tpg_s_xfer_func(struct tpg_data *tpg, u32 xfer_func)
{
if (tpg->xfer_func == xfer_func)
return;
tpg->xfer_func = xfer_func;
tpg->recalc_colors = true;
}
static inline u32 tpg_g_xfer_func(const struct tpg_data *tpg)
{
return tpg->xfer_func;
}
static inline void tpg_s_quantization(struct tpg_data *tpg, u32 quantization)
{
if (tpg->quantization == quantization)
return;
tpg->quantization = quantization;
tpg->recalc_colors = true;
}
static inline u32 tpg_g_quantization(const struct tpg_data *tpg)
{
return tpg->quantization;
}
static inline unsigned tpg_g_buffers(const struct tpg_data *tpg)
{
return tpg->buffers;
}
static inline unsigned tpg_g_planes(const struct tpg_data *tpg)
{
return tpg->interleaved ? 1 : tpg->planes;
}
static inline bool tpg_g_interleaved(const struct tpg_data *tpg)
{
return tpg->interleaved;
}
static inline unsigned tpg_g_twopixelsize(const struct tpg_data *tpg, unsigned plane)
{
return tpg->twopixelsize[plane];
}
static inline unsigned tpg_hdiv(const struct tpg_data *tpg,
unsigned plane, unsigned x)
{
return ((x / tpg->hdownsampling[plane]) & tpg->hmask[plane]) *
tpg->twopixelsize[plane] / 2;
}
static inline unsigned tpg_hscale(const struct tpg_data *tpg, unsigned x)
{
return (x * tpg->scaled_width) / tpg->src_width;
}
static inline unsigned tpg_hscale_div(const struct tpg_data *tpg,
unsigned plane, unsigned x)
{
return tpg_hdiv(tpg, plane, tpg_hscale(tpg, x));
}
static inline unsigned tpg_g_bytesperline(const struct tpg_data *tpg, unsigned plane)
{
return tpg->bytesperline[plane];
}
static inline void tpg_s_bytesperline(struct tpg_data *tpg, unsigned plane, unsigned bpl)
{
unsigned p;
if (tpg->buffers > 1) {
tpg->bytesperline[plane] = bpl;
return;
}
for (p = 0; p < tpg_g_planes(tpg); p++) {
unsigned plane_w = bpl * tpg->twopixelsize[p] / tpg->twopixelsize[0];
tpg->bytesperline[p] = plane_w / tpg->hdownsampling[p];
}
if (tpg_g_interleaved(tpg))
tpg->bytesperline[1] = tpg->bytesperline[0];
}
static inline unsigned tpg_g_line_width(const struct tpg_data *tpg, unsigned plane)
{
unsigned w = 0;
unsigned p;
if (tpg->buffers > 1)
return tpg_g_bytesperline(tpg, plane);
for (p = 0; p < tpg_g_planes(tpg); p++) {
unsigned plane_w = tpg_g_bytesperline(tpg, p);
w += plane_w / tpg->vdownsampling[p];
}
return w;
}
static inline unsigned tpg_calc_line_width(const struct tpg_data *tpg,
unsigned plane, unsigned bpl)
{
unsigned w = 0;
unsigned p;
if (tpg->buffers > 1)
return bpl;
for (p = 0; p < tpg_g_planes(tpg); p++) {
unsigned plane_w = bpl * tpg->twopixelsize[p] / tpg->twopixelsize[0];
plane_w /= tpg->hdownsampling[p];
w += plane_w / tpg->vdownsampling[p];
}
return w;
}
static inline unsigned tpg_calc_plane_size(const struct tpg_data *tpg, unsigned plane)
{
if (plane >= tpg_g_planes(tpg))
return 0;
return tpg_g_bytesperline(tpg, plane) * tpg->buf_height /
tpg->vdownsampling[plane];
}
static inline void tpg_s_buf_height(struct tpg_data *tpg, unsigned h)
{
tpg->buf_height = h;
}
static inline void tpg_s_field(struct tpg_data *tpg, unsigned field, bool alternate)
{
tpg->field = field;
tpg->field_alternate = alternate;
}
static inline void tpg_s_perc_fill(struct tpg_data *tpg,
unsigned perc_fill)
{
tpg->perc_fill = perc_fill;
}
static inline unsigned tpg_g_perc_fill(const struct tpg_data *tpg)
{
return tpg->perc_fill;
}
static inline void tpg_s_perc_fill_blank(struct tpg_data *tpg,
bool perc_fill_blank)
{
tpg->perc_fill_blank = perc_fill_blank;
}
static inline void tpg_s_video_aspect(struct tpg_data *tpg,
enum tpg_video_aspect vid_aspect)
{
if (tpg->vid_aspect == vid_aspect)
return;
tpg->vid_aspect = vid_aspect;
tpg->recalc_square_border = true;
}
static inline enum tpg_video_aspect tpg_g_video_aspect(const struct tpg_data *tpg)
{
return tpg->vid_aspect;
}
static inline void tpg_s_pixel_aspect(struct tpg_data *tpg,
enum tpg_pixel_aspect pix_aspect)
{
if (tpg->pix_aspect == pix_aspect)
return;
tpg->pix_aspect = pix_aspect;
tpg->recalc_square_border = true;
}
static inline void tpg_s_show_border(struct tpg_data *tpg,
bool show_border)
{
tpg->show_border = show_border;
}
static inline void tpg_s_show_square(struct tpg_data *tpg,
bool show_square)
{
tpg->show_square = show_square;
}
static inline void tpg_s_insert_sav(struct tpg_data *tpg, bool insert_sav)
{
tpg->insert_sav = insert_sav;
}
static inline void tpg_s_insert_eav(struct tpg_data *tpg, bool insert_eav)
{
tpg->insert_eav = insert_eav;
}
void tpg_update_mv_step(struct tpg_data *tpg);
static inline void tpg_s_mv_hor_mode(struct tpg_data *tpg,
enum tpg_move_mode mv_hor_mode)
{
tpg->mv_hor_mode = mv_hor_mode;
tpg_update_mv_step(tpg);
}
static inline void tpg_s_mv_vert_mode(struct tpg_data *tpg,
enum tpg_move_mode mv_vert_mode)
{
tpg->mv_vert_mode = mv_vert_mode;
tpg_update_mv_step(tpg);
}
static inline void tpg_init_mv_count(struct tpg_data *tpg)
{
tpg->mv_hor_count = tpg->mv_vert_count = 0;
}
static inline void tpg_update_mv_count(struct tpg_data *tpg, bool frame_is_field)
{
tpg->mv_hor_count += tpg->mv_hor_step * (frame_is_field ? 1 : 2);
tpg->mv_vert_count += tpg->mv_vert_step * (frame_is_field ? 1 : 2);
}
static inline void tpg_s_hflip(struct tpg_data *tpg, bool hflip)
{
if (tpg->hflip == hflip)
return;
tpg->hflip = hflip;
tpg_update_mv_step(tpg);
tpg->recalc_lines = true;
}
static inline bool tpg_g_hflip(const struct tpg_data *tpg)
{
return tpg->hflip;
}
static inline void tpg_s_vflip(struct tpg_data *tpg, bool vflip)
{
tpg->vflip = vflip;
}
static inline bool tpg_g_vflip(const struct tpg_data *tpg)
{
return tpg->vflip;
}
static inline bool tpg_pattern_is_static(const struct tpg_data *tpg)
{
return tpg->pattern != TPG_PAT_NOISE &&
tpg->mv_hor_mode == TPG_MOVE_NONE &&
tpg->mv_vert_mode == TPG_MOVE_NONE;
}
#endif

View file

@ -26,6 +26,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-rect.h>
#include "vivid-core.h"
#include "vivid-vid-common.h"
@ -590,16 +591,16 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
} else {
struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor };
rect_set_min_size(&r, &vivid_min_rect);
rect_set_max_size(&r, &vivid_max_rect);
v4l2_rect_set_min_size(&r, &vivid_min_rect);
v4l2_rect_set_max_size(&r, &vivid_max_rect);
if (dev->has_scaler_cap && !dev->has_compose_cap) {
struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h };
rect_set_max_size(&r, &max_r);
v4l2_rect_set_max_size(&r, &max_r);
} else if (!dev->has_scaler_cap && dev->has_crop_cap && !dev->has_compose_cap) {
rect_set_max_size(&r, &dev->src_rect);
v4l2_rect_set_max_size(&r, &dev->src_rect);
} else if (!dev->has_scaler_cap && !dev->has_crop_cap) {
rect_set_min_size(&r, &dev->src_rect);
v4l2_rect_set_min_size(&r, &dev->src_rect);
}
mp->width = r.width;
mp->height = r.height / factor;
@ -668,7 +669,7 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv,
if (dev->has_scaler_cap) {
if (dev->has_compose_cap)
rect_map_inside(compose, &r);
v4l2_rect_map_inside(compose, &r);
else
*compose = r;
if (dev->has_crop_cap && !dev->has_compose_cap) {
@ -683,9 +684,9 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv,
factor * r.height * MAX_ZOOM
};
rect_set_min_size(crop, &min_r);
rect_set_max_size(crop, &max_r);
rect_map_inside(crop, &dev->crop_bounds_cap);
v4l2_rect_set_min_size(crop, &min_r);
v4l2_rect_set_max_size(crop, &max_r);
v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
} else if (dev->has_crop_cap) {
struct v4l2_rect min_r = {
0, 0,
@ -698,27 +699,27 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv,
factor * compose->height * MAX_ZOOM
};
rect_set_min_size(crop, &min_r);
rect_set_max_size(crop, &max_r);
rect_map_inside(crop, &dev->crop_bounds_cap);
v4l2_rect_set_min_size(crop, &min_r);
v4l2_rect_set_max_size(crop, &max_r);
v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
}
} else if (dev->has_crop_cap && !dev->has_compose_cap) {
r.height *= factor;
rect_set_size_to(crop, &r);
rect_map_inside(crop, &dev->crop_bounds_cap);
v4l2_rect_set_size_to(crop, &r);
v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
r = *crop;
r.height /= factor;
rect_set_size_to(compose, &r);
v4l2_rect_set_size_to(compose, &r);
} else if (!dev->has_crop_cap) {
rect_map_inside(compose, &r);
v4l2_rect_map_inside(compose, &r);
} else {
r.height *= factor;
rect_set_max_size(crop, &r);
rect_map_inside(crop, &dev->crop_bounds_cap);
v4l2_rect_set_max_size(crop, &r);
v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
compose->top *= factor;
compose->height *= factor;
rect_set_size_to(compose, crop);
rect_map_inside(compose, &r);
v4l2_rect_set_size_to(compose, crop);
v4l2_rect_map_inside(compose, &r);
compose->top /= factor;
compose->height /= factor;
}
@ -735,9 +736,9 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv,
} else {
struct v4l2_rect r = { 0, 0, mp->width, mp->height };
rect_set_size_to(compose, &r);
v4l2_rect_set_size_to(compose, &r);
r.height *= factor;
rect_set_size_to(crop, &r);
v4l2_rect_set_size_to(crop, &r);
}
dev->fmt_cap_rect.width = mp->width;
@ -886,9 +887,9 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection
ret = vivid_vid_adjust_sel(s->flags, &s->r);
if (ret)
return ret;
rect_set_min_size(&s->r, &vivid_min_rect);
rect_set_max_size(&s->r, &dev->src_rect);
rect_map_inside(&s->r, &dev->crop_bounds_cap);
v4l2_rect_set_min_size(&s->r, &vivid_min_rect);
v4l2_rect_set_max_size(&s->r, &dev->src_rect);
v4l2_rect_map_inside(&s->r, &dev->crop_bounds_cap);
s->r.top /= factor;
s->r.height /= factor;
if (dev->has_scaler_cap) {
@ -904,36 +905,36 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection
s->r.height / MAX_ZOOM
};
rect_set_min_size(&fmt, &min_rect);
v4l2_rect_set_min_size(&fmt, &min_rect);
if (!dev->has_compose_cap)
rect_set_max_size(&fmt, &max_rect);
if (!rect_same_size(&dev->fmt_cap_rect, &fmt) &&
v4l2_rect_set_max_size(&fmt, &max_rect);
if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) &&
vb2_is_busy(&dev->vb_vid_cap_q))
return -EBUSY;
if (dev->has_compose_cap) {
rect_set_min_size(compose, &min_rect);
rect_set_max_size(compose, &max_rect);
v4l2_rect_set_min_size(compose, &min_rect);
v4l2_rect_set_max_size(compose, &max_rect);
}
dev->fmt_cap_rect = fmt;
tpg_s_buf_height(&dev->tpg, fmt.height);
} else if (dev->has_compose_cap) {
struct v4l2_rect fmt = dev->fmt_cap_rect;
rect_set_min_size(&fmt, &s->r);
if (!rect_same_size(&dev->fmt_cap_rect, &fmt) &&
v4l2_rect_set_min_size(&fmt, &s->r);
if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) &&
vb2_is_busy(&dev->vb_vid_cap_q))
return -EBUSY;
dev->fmt_cap_rect = fmt;
tpg_s_buf_height(&dev->tpg, fmt.height);
rect_set_size_to(compose, &s->r);
rect_map_inside(compose, &dev->fmt_cap_rect);
v4l2_rect_set_size_to(compose, &s->r);
v4l2_rect_map_inside(compose, &dev->fmt_cap_rect);
} else {
if (!rect_same_size(&s->r, &dev->fmt_cap_rect) &&
if (!v4l2_rect_same_size(&s->r, &dev->fmt_cap_rect) &&
vb2_is_busy(&dev->vb_vid_cap_q))
return -EBUSY;
rect_set_size_to(&dev->fmt_cap_rect, &s->r);
rect_set_size_to(compose, &s->r);
rect_map_inside(compose, &dev->fmt_cap_rect);
v4l2_rect_set_size_to(&dev->fmt_cap_rect, &s->r);
v4l2_rect_set_size_to(compose, &s->r);
v4l2_rect_map_inside(compose, &dev->fmt_cap_rect);
tpg_s_buf_height(&dev->tpg, dev->fmt_cap_rect.height);
}
s->r.top *= factor;
@ -946,8 +947,8 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection
ret = vivid_vid_adjust_sel(s->flags, &s->r);
if (ret)
return ret;
rect_set_min_size(&s->r, &vivid_min_rect);
rect_set_max_size(&s->r, &dev->fmt_cap_rect);
v4l2_rect_set_min_size(&s->r, &vivid_min_rect);
v4l2_rect_set_max_size(&s->r, &dev->fmt_cap_rect);
if (dev->has_scaler_cap) {
struct v4l2_rect max_rect = {
0, 0,
@ -955,7 +956,7 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection
(dev->src_rect.height / factor) * MAX_ZOOM
};
rect_set_max_size(&s->r, &max_rect);
v4l2_rect_set_max_size(&s->r, &max_rect);
if (dev->has_crop_cap) {
struct v4l2_rect min_rect = {
0, 0,
@ -968,23 +969,23 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection
(s->r.height * factor) * MAX_ZOOM
};
rect_set_min_size(crop, &min_rect);
rect_set_max_size(crop, &max_rect);
rect_map_inside(crop, &dev->crop_bounds_cap);
v4l2_rect_set_min_size(crop, &min_rect);
v4l2_rect_set_max_size(crop, &max_rect);
v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
}
} else if (dev->has_crop_cap) {
s->r.top *= factor;
s->r.height *= factor;
rect_set_max_size(&s->r, &dev->src_rect);
rect_set_size_to(crop, &s->r);
rect_map_inside(crop, &dev->crop_bounds_cap);
v4l2_rect_set_max_size(&s->r, &dev->src_rect);
v4l2_rect_set_size_to(crop, &s->r);
v4l2_rect_map_inside(crop, &dev->crop_bounds_cap);
s->r.top /= factor;
s->r.height /= factor;
} else {
rect_set_size_to(&s->r, &dev->src_rect);
v4l2_rect_set_size_to(&s->r, &dev->src_rect);
s->r.height /= factor;
}
rect_map_inside(&s->r, &dev->fmt_cap_rect);
v4l2_rect_map_inside(&s->r, &dev->fmt_cap_rect);
if (dev->bitmap_cap && (compose->width != s->r.width ||
compose->height != s->r.height)) {
kfree(dev->bitmap_cap);
@ -1124,7 +1125,7 @@ int vidioc_try_fmt_vid_overlay(struct file *file, void *priv,
for (j = i + 1; j < win->clipcount; j++) {
struct v4l2_rect *r2 = &dev->try_clips_cap[j].c;
if (rect_overlap(r1, r2))
if (v4l2_rect_overlap(r1, r2))
return -EINVAL;
}
}

View file

@ -653,103 +653,6 @@ int fmt_sp2mp_func(struct file *file, void *priv,
return ret;
}
/* v4l2_rect helper function: copy the width/height values */
void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size)
{
r->width = size->width;
r->height = size->height;
}
/* v4l2_rect helper function: width and height of r should be >= min_size */
void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size)
{
if (r->width < min_size->width)
r->width = min_size->width;
if (r->height < min_size->height)
r->height = min_size->height;
}
/* v4l2_rect helper function: width and height of r should be <= max_size */
void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size)
{
if (r->width > max_size->width)
r->width = max_size->width;
if (r->height > max_size->height)
r->height = max_size->height;
}
/* v4l2_rect helper function: r should be inside boundary */
void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary)
{
rect_set_max_size(r, boundary);
if (r->left < boundary->left)
r->left = boundary->left;
if (r->top < boundary->top)
r->top = boundary->top;
if (r->left + r->width > boundary->width)
r->left = boundary->width - r->width;
if (r->top + r->height > boundary->height)
r->top = boundary->height - r->height;
}
/* v4l2_rect helper function: return true if r1 has the same size as r2 */
bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
{
return r1->width == r2->width && r1->height == r2->height;
}
/* v4l2_rect helper function: calculate the intersection of two rects */
struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b)
{
struct v4l2_rect r;
int right, bottom;
r.top = max(a->top, b->top);
r.left = max(a->left, b->left);
bottom = min(a->top + a->height, b->top + b->height);
right = min(a->left + a->width, b->left + b->width);
r.height = max(0, bottom - r.top);
r.width = max(0, right - r.left);
return r;
}
/*
* v4l2_rect helper function: scale rect r by to->width / from->width and
* to->height / from->height.
*/
void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from,
const struct v4l2_rect *to)
{
if (from->width == 0 || from->height == 0) {
r->left = r->top = r->width = r->height = 0;
return;
}
r->left = (((r->left - from->left) * to->width) / from->width) & ~1;
r->width = ((r->width * to->width) / from->width) & ~1;
r->top = ((r->top - from->top) * to->height) / from->height;
r->height = (r->height * to->height) / from->height;
}
bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
{
/*
* IF the left side of r1 is to the right of the right side of r2 OR
* the left side of r2 is to the right of the right side of r1 THEN
* they do not overlap.
*/
if (r1->left >= r2->left + r2->width ||
r2->left >= r1->left + r1->width)
return false;
/*
* IF the top side of r1 is below the bottom of r2 OR
* the top side of r2 is below the bottom of r1 THEN
* they do not overlap.
*/
if (r1->top >= r2->top + r2->height ||
r2->top >= r1->top + r1->height)
return false;
return true;
}
int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r)
{
unsigned w = r->width;

View file

@ -37,15 +37,6 @@ const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat)
bool vivid_vid_can_loop(struct vivid_dev *dev);
void vivid_send_source_change(struct vivid_dev *dev, unsigned type);
bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2);
void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size);
void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size);
void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size);
void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary);
bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2);
struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b);
void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from,
const struct v4l2_rect *to);
int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r);
int vivid_enum_fmt_vid(struct file *file, void *priv, struct v4l2_fmtdesc *f);

View file

@ -25,6 +25,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-rect.h>
#include "vivid-core.h"
#include "vivid-vid-common.h"
@ -376,16 +377,16 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv,
} else {
struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor };
rect_set_min_size(&r, &vivid_min_rect);
rect_set_max_size(&r, &vivid_max_rect);
v4l2_rect_set_min_size(&r, &vivid_min_rect);
v4l2_rect_set_max_size(&r, &vivid_max_rect);
if (dev->has_scaler_out && !dev->has_crop_out) {
struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h };
rect_set_max_size(&r, &max_r);
v4l2_rect_set_max_size(&r, &max_r);
} else if (!dev->has_scaler_out && dev->has_compose_out && !dev->has_crop_out) {
rect_set_max_size(&r, &dev->sink_rect);
v4l2_rect_set_max_size(&r, &dev->sink_rect);
} else if (!dev->has_scaler_out && !dev->has_compose_out) {
rect_set_min_size(&r, &dev->sink_rect);
v4l2_rect_set_min_size(&r, &dev->sink_rect);
}
mp->width = r.width;
mp->height = r.height / factor;
@ -473,7 +474,7 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv,
if (dev->has_scaler_out) {
if (dev->has_crop_out)
rect_map_inside(crop, &r);
v4l2_rect_map_inside(crop, &r);
else
*crop = r;
if (dev->has_compose_out && !dev->has_crop_out) {
@ -488,9 +489,9 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv,
factor * r.height * MAX_ZOOM
};
rect_set_min_size(compose, &min_r);
rect_set_max_size(compose, &max_r);
rect_map_inside(compose, &dev->compose_bounds_out);
v4l2_rect_set_min_size(compose, &min_r);
v4l2_rect_set_max_size(compose, &max_r);
v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
} else if (dev->has_compose_out) {
struct v4l2_rect min_r = {
0, 0,
@ -503,36 +504,36 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv,
factor * crop->height * MAX_ZOOM
};
rect_set_min_size(compose, &min_r);
rect_set_max_size(compose, &max_r);
rect_map_inside(compose, &dev->compose_bounds_out);
v4l2_rect_set_min_size(compose, &min_r);
v4l2_rect_set_max_size(compose, &max_r);
v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
}
} else if (dev->has_compose_out && !dev->has_crop_out) {
rect_set_size_to(crop, &r);
v4l2_rect_set_size_to(crop, &r);
r.height *= factor;
rect_set_size_to(compose, &r);
rect_map_inside(compose, &dev->compose_bounds_out);
v4l2_rect_set_size_to(compose, &r);
v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
} else if (!dev->has_compose_out) {
rect_map_inside(crop, &r);
v4l2_rect_map_inside(crop, &r);
r.height /= factor;
rect_set_size_to(compose, &r);
v4l2_rect_set_size_to(compose, &r);
} else {
r.height *= factor;
rect_set_max_size(compose, &r);
rect_map_inside(compose, &dev->compose_bounds_out);
v4l2_rect_set_max_size(compose, &r);
v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
crop->top *= factor;
crop->height *= factor;
rect_set_size_to(crop, compose);
rect_map_inside(crop, &r);
v4l2_rect_set_size_to(crop, compose);
v4l2_rect_map_inside(crop, &r);
crop->top /= factor;
crop->height /= factor;
}
} else {
struct v4l2_rect r = { 0, 0, mp->width, mp->height };
rect_set_size_to(crop, &r);
v4l2_rect_set_size_to(crop, &r);
r.height /= factor;
rect_set_size_to(compose, &r);
v4l2_rect_set_size_to(compose, &r);
}
dev->fmt_out_rect.width = mp->width;
@ -683,8 +684,8 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection
ret = vivid_vid_adjust_sel(s->flags, &s->r);
if (ret)
return ret;
rect_set_min_size(&s->r, &vivid_min_rect);
rect_set_max_size(&s->r, &dev->fmt_out_rect);
v4l2_rect_set_min_size(&s->r, &vivid_min_rect);
v4l2_rect_set_max_size(&s->r, &dev->fmt_out_rect);
if (dev->has_scaler_out) {
struct v4l2_rect max_rect = {
0, 0,
@ -692,7 +693,7 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection
(dev->sink_rect.height / factor) * MAX_ZOOM
};
rect_set_max_size(&s->r, &max_rect);
v4l2_rect_set_max_size(&s->r, &max_rect);
if (dev->has_compose_out) {
struct v4l2_rect min_rect = {
0, 0,
@ -705,23 +706,23 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection
(s->r.height * factor) * MAX_ZOOM
};
rect_set_min_size(compose, &min_rect);
rect_set_max_size(compose, &max_rect);
rect_map_inside(compose, &dev->compose_bounds_out);
v4l2_rect_set_min_size(compose, &min_rect);
v4l2_rect_set_max_size(compose, &max_rect);
v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
}
} else if (dev->has_compose_out) {
s->r.top *= factor;
s->r.height *= factor;
rect_set_max_size(&s->r, &dev->sink_rect);
rect_set_size_to(compose, &s->r);
rect_map_inside(compose, &dev->compose_bounds_out);
v4l2_rect_set_max_size(&s->r, &dev->sink_rect);
v4l2_rect_set_size_to(compose, &s->r);
v4l2_rect_map_inside(compose, &dev->compose_bounds_out);
s->r.top /= factor;
s->r.height /= factor;
} else {
rect_set_size_to(&s->r, &dev->sink_rect);
v4l2_rect_set_size_to(&s->r, &dev->sink_rect);
s->r.height /= factor;
}
rect_map_inside(&s->r, &dev->fmt_out_rect);
v4l2_rect_map_inside(&s->r, &dev->fmt_out_rect);
*crop = s->r;
break;
case V4L2_SEL_TGT_COMPOSE:
@ -730,9 +731,9 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection
ret = vivid_vid_adjust_sel(s->flags, &s->r);
if (ret)
return ret;
rect_set_min_size(&s->r, &vivid_min_rect);
rect_set_max_size(&s->r, &dev->sink_rect);
rect_map_inside(&s->r, &dev->compose_bounds_out);
v4l2_rect_set_min_size(&s->r, &vivid_min_rect);
v4l2_rect_set_max_size(&s->r, &dev->sink_rect);
v4l2_rect_map_inside(&s->r, &dev->compose_bounds_out);
s->r.top /= factor;
s->r.height /= factor;
if (dev->has_scaler_out) {
@ -748,35 +749,35 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection
s->r.height / MAX_ZOOM
};
rect_set_min_size(&fmt, &min_rect);
v4l2_rect_set_min_size(&fmt, &min_rect);
if (!dev->has_crop_out)
rect_set_max_size(&fmt, &max_rect);
if (!rect_same_size(&dev->fmt_out_rect, &fmt) &&
v4l2_rect_set_max_size(&fmt, &max_rect);
if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) &&
vb2_is_busy(&dev->vb_vid_out_q))
return -EBUSY;
if (dev->has_crop_out) {
rect_set_min_size(crop, &min_rect);
rect_set_max_size(crop, &max_rect);
v4l2_rect_set_min_size(crop, &min_rect);
v4l2_rect_set_max_size(crop, &max_rect);
}
dev->fmt_out_rect = fmt;
} else if (dev->has_crop_out) {
struct v4l2_rect fmt = dev->fmt_out_rect;
rect_set_min_size(&fmt, &s->r);
if (!rect_same_size(&dev->fmt_out_rect, &fmt) &&
v4l2_rect_set_min_size(&fmt, &s->r);
if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) &&
vb2_is_busy(&dev->vb_vid_out_q))
return -EBUSY;
dev->fmt_out_rect = fmt;
rect_set_size_to(crop, &s->r);
rect_map_inside(crop, &dev->fmt_out_rect);
v4l2_rect_set_size_to(crop, &s->r);
v4l2_rect_map_inside(crop, &dev->fmt_out_rect);
} else {
if (!rect_same_size(&s->r, &dev->fmt_out_rect) &&
if (!v4l2_rect_same_size(&s->r, &dev->fmt_out_rect) &&
vb2_is_busy(&dev->vb_vid_out_q))
return -EBUSY;
rect_set_size_to(&dev->fmt_out_rect, &s->r);
rect_set_size_to(crop, &s->r);
v4l2_rect_set_size_to(&dev->fmt_out_rect, &s->r);
v4l2_rect_set_size_to(crop, &s->r);
crop->height /= factor;
rect_map_inside(crop, &dev->fmt_out_rect);
v4l2_rect_map_inside(crop, &dev->fmt_out_rect);
}
s->r.top *= factor;
s->r.height *= factor;
@ -901,7 +902,7 @@ int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv,
for (j = i + 1; j < win->clipcount; j++) {
struct v4l2_rect *r2 = &dev->try_clips_out[j].c;
if (rect_overlap(r1, r2))
if (v4l2_rect_overlap(r1, r2))
return -EINVAL;
}
}

View file

@ -26,7 +26,6 @@
struct clk;
struct device;
struct vsp1_dl;
struct vsp1_drm;
struct vsp1_entity;
struct vsp1_platform_data;
@ -49,6 +48,7 @@ struct vsp1_uds;
struct vsp1_device_info {
u32 version;
unsigned int gen;
unsigned int features;
unsigned int rpf_count;
unsigned int uds_count;
@ -85,8 +85,6 @@ struct vsp1_device {
struct media_entity_operations media_ops;
struct vsp1_drm *drm;
bool use_dl;
};
int vsp1_device_get(struct vsp1_device *vsp1);
@ -104,14 +102,4 @@ static inline void vsp1_write(struct vsp1_device *vsp1, u32 reg, u32 data)
iowrite32(data, vsp1->mmio + reg);
}
#include "vsp1_dl.h"
static inline void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data)
{
if (e->vsp1->use_dl)
vsp1_dl_add(e, reg, data);
else
vsp1_write(e->vsp1, reg, data);
}
#endif /* __VSP1_H__ */

View file

@ -18,6 +18,8 @@
#include "vsp1.h"
#include "vsp1_bru.h"
#include "vsp1_dl.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
@ -28,9 +30,10 @@
* Device Access
*/
static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data)
static inline void vsp1_bru_write(struct vsp1_bru *bru, struct vsp1_dl_list *dl,
u32 reg, u32 data)
{
vsp1_mod_write(&bru->entity, reg, data);
vsp1_dl_list_write(dl, reg, data);
}
/* -----------------------------------------------------------------------------
@ -42,13 +45,9 @@ static int bru_s_ctrl(struct v4l2_ctrl *ctrl)
struct vsp1_bru *bru =
container_of(ctrl->handler, struct vsp1_bru, ctrls);
if (!vsp1_entity_is_streaming(&bru->entity))
return 0;
switch (ctrl->id) {
case V4L2_CID_BG_COLOR:
vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, ctrl->val |
(0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
bru->bgcolor = ctrl->val;
break;
}
@ -60,26 +59,225 @@ static const struct v4l2_ctrl_ops bru_ctrl_ops = {
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
* V4L2 Subdevice Operations
*/
static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
/*
* The BRU can't perform format conversion, all sink and source formats must be
* identical. We pick the format on the first sink pad (pad 0) and propagate it
* to all other pads.
*/
static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
MEDIA_BUS_FMT_ARGB8888_1X32,
MEDIA_BUS_FMT_AYUV8_1X32,
};
return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
ARRAY_SIZE(codes));
}
static int bru_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
if (fse->index)
return -EINVAL;
if (fse->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fse->code != MEDIA_BUS_FMT_AYUV8_1X32)
return -EINVAL;
fse->min_width = BRU_MIN_SIZE;
fse->max_width = BRU_MAX_SIZE;
fse->min_height = BRU_MIN_SIZE;
fse->max_height = BRU_MAX_SIZE;
return 0;
}
static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad)
{
return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, pad);
}
static void bru_try_format(struct vsp1_bru *bru,
struct v4l2_subdev_pad_config *config,
unsigned int pad, struct v4l2_mbus_framefmt *fmt)
{
struct v4l2_mbus_framefmt *format;
switch (pad) {
case BRU_PAD_SINK(0):
/* Default to YUV if the requested format is not supported. */
if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
break;
default:
/* The BRU can't perform format conversion. */
format = vsp1_entity_get_pad_format(&bru->entity, config,
BRU_PAD_SINK(0));
fmt->code = format->code;
break;
}
fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE);
fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE);
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int bru_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity);
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which);
if (!config)
return -EINVAL;
bru_try_format(bru, config, fmt->pad, &fmt->format);
format = vsp1_entity_get_pad_format(&bru->entity, config, fmt->pad);
*format = fmt->format;
/* Reset the compose rectangle */
if (fmt->pad != bru->entity.source_pad) {
struct v4l2_rect *compose;
compose = bru_get_compose(bru, config, fmt->pad);
compose->left = 0;
compose->top = 0;
compose->width = format->width;
compose->height = format->height;
}
/* Propagate the format code to all pads */
if (fmt->pad == BRU_PAD_SINK(0)) {
unsigned int i;
for (i = 0; i <= bru->entity.source_pad; ++i) {
format = vsp1_entity_get_pad_format(&bru->entity,
config, i);
format->code = fmt->format.code;
}
}
return 0;
}
static int bru_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_subdev_pad_config *config;
if (sel->pad == bru->entity.source_pad)
return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = BRU_MAX_SIZE;
sel->r.height = BRU_MAX_SIZE;
return 0;
case V4L2_SEL_TGT_COMPOSE:
config = vsp1_entity_get_pad_config(&bru->entity, cfg,
sel->which);
if (!config)
return -EINVAL;
sel->r = *bru_get_compose(bru, config, sel->pad);
return 0;
default:
return -EINVAL;
}
}
static int bru_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *compose;
if (sel->pad == bru->entity.source_pad)
return -EINVAL;
if (sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which);
if (!config)
return -EINVAL;
/* The compose rectangle top left corner must be inside the output
* frame.
*/
format = vsp1_entity_get_pad_format(&bru->entity, config,
bru->entity.source_pad);
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
/* Scaling isn't supported, the compose rectangle size must be identical
* to the sink format size.
*/
format = vsp1_entity_get_pad_format(&bru->entity, config, sel->pad);
sel->r.width = format->width;
sel->r.height = format->height;
compose = bru_get_compose(bru, config, sel->pad);
*compose = sel->r;
return 0;
}
static struct v4l2_subdev_pad_ops bru_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = bru_enum_mbus_code,
.enum_frame_size = bru_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = bru_set_format,
.get_selection = bru_get_selection,
.set_selection = bru_set_selection,
};
static struct v4l2_subdev_ops bru_ops = {
.pad = &bru_pad_ops,
};
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
static void bru_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
{
struct vsp1_bru *bru = to_bru(&entity->subdev);
struct v4l2_mbus_framefmt *format;
unsigned int flags;
unsigned int i;
int ret;
ret = vsp1_entity_set_streaming(&bru->entity, enable);
if (ret < 0)
return ret;
if (!enable)
return 0;
format = &bru->entity.formats[bru->entity.source_pad];
format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config,
bru->entity.source_pad);
/* The hardware is extremely flexible but we have no userspace API to
* expose all the parameters, nor is it clear whether we would have use
@ -91,21 +289,26 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
* format at the pipeline output is premultiplied.
*/
flags = pipe->output ? pipe->output->format.flags : 0;
vsp1_bru_write(bru, VI6_BRU_INCTRL,
vsp1_bru_write(bru, dl, VI6_BRU_INCTRL,
flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
0 : VI6_BRU_INCTRL_NRM);
/* Set the background position to cover the whole output image. */
vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE,
/* Set the background position to cover the whole output image and
* configure its color.
*/
vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_SIZE,
(format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
(format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0);
vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_LOC, 0);
vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_COL, bru->bgcolor |
(0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
/* Route BRU input 1 as SRC input to the ROP unit and configure the ROP
* unit with a NOP operation to make BRU input 1 available as the
* Blend/ROP unit B SRC input.
*/
vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) |
vsp1_bru_write(bru, dl, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) |
VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
VI6_BRU_ROP_AROP(VI6_ROP_NOP));
@ -142,7 +345,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
if (i != 1)
ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl);
vsp1_bru_write(bru, dl, VI6_BRU_CTRL(i), ctrl);
/* Harcode the blending formula to
*
@ -156,7 +359,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
*
* otherwise.
*/
vsp1_bru_write(bru, VI6_BRU_BLD(i),
vsp1_bru_write(bru, dl, VI6_BRU_BLD(i),
VI6_BRU_BLD_CCMDX_255_SRC_A |
(premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
VI6_BRU_BLD_CCMDY_SRC_A) |
@ -164,239 +367,10 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
VI6_BRU_BLD_ACMDY_COEFY |
(0xff << VI6_BRU_BLD_COEFY_SHIFT));
}
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
/*
* The BRU can't perform format conversion, all sink and source formats must be
* identical. We pick the format on the first sink pad (pad 0) and propagate it
* to all other pads.
*/
static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
MEDIA_BUS_FMT_ARGB8888_1X32,
MEDIA_BUS_FMT_AYUV8_1X32,
};
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_mbus_framefmt *format;
if (code->pad == BRU_PAD_SINK(0)) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
if (code->index)
return -EINVAL;
format = vsp1_entity_get_pad_format(&bru->entity, cfg,
BRU_PAD_SINK(0), code->which);
code->code = format->code;
}
return 0;
}
static int bru_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
if (fse->index)
return -EINVAL;
if (fse->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fse->code != MEDIA_BUS_FMT_AYUV8_1X32)
return -EINVAL;
fse->min_width = BRU_MIN_SIZE;
fse->max_width = BRU_MAX_SIZE;
fse->min_height = BRU_MIN_SIZE;
fse->max_height = BRU_MAX_SIZE;
return 0;
}
static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad, u32 which)
{
switch (which) {
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_crop(&bru->entity.subdev, cfg, pad);
case V4L2_SUBDEV_FORMAT_ACTIVE:
return &bru->inputs[pad].compose;
default:
return NULL;
}
}
static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_bru *bru = to_bru(subdev);
fmt->format = *vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad,
fmt->which);
return 0;
}
static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_pad_config *cfg,
unsigned int pad, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
struct v4l2_mbus_framefmt *format;
switch (pad) {
case BRU_PAD_SINK(0):
/* Default to YUV if the requested format is not supported. */
if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
break;
default:
/* The BRU can't perform format conversion. */
format = vsp1_entity_get_pad_format(&bru->entity, cfg,
BRU_PAD_SINK(0), which);
fmt->code = format->code;
break;
}
fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE);
fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE);
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_mbus_framefmt *format;
bru_try_format(bru, cfg, fmt->pad, &fmt->format, fmt->which);
format = vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad,
fmt->which);
*format = fmt->format;
/* Reset the compose rectangle */
if (fmt->pad != bru->entity.source_pad) {
struct v4l2_rect *compose;
compose = bru_get_compose(bru, cfg, fmt->pad, fmt->which);
compose->left = 0;
compose->top = 0;
compose->width = format->width;
compose->height = format->height;
}
/* Propagate the format code to all pads */
if (fmt->pad == BRU_PAD_SINK(0)) {
unsigned int i;
for (i = 0; i <= bru->entity.source_pad; ++i) {
format = vsp1_entity_get_pad_format(&bru->entity, cfg,
i, fmt->which);
format->code = fmt->format.code;
}
}
return 0;
}
static int bru_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
struct vsp1_bru *bru = to_bru(subdev);
if (sel->pad == bru->entity.source_pad)
return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = BRU_MAX_SIZE;
sel->r.height = BRU_MAX_SIZE;
return 0;
case V4L2_SEL_TGT_COMPOSE:
sel->r = *bru_get_compose(bru, cfg, sel->pad, sel->which);
return 0;
default:
return -EINVAL;
}
}
static int bru_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *compose;
if (sel->pad == bru->entity.source_pad)
return -EINVAL;
if (sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
/* The compose rectangle top left corner must be inside the output
* frame.
*/
format = vsp1_entity_get_pad_format(&bru->entity, cfg,
bru->entity.source_pad, sel->which);
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
/* Scaling isn't supported, the compose rectangle size must be identical
* to the sink format size.
*/
format = vsp1_entity_get_pad_format(&bru->entity, cfg, sel->pad,
sel->which);
sel->r.width = format->width;
sel->r.height = format->height;
compose = bru_get_compose(bru, cfg, sel->pad, sel->which);
*compose = sel->r;
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops bru_video_ops = {
.s_stream = bru_s_stream,
};
static struct v4l2_subdev_pad_ops bru_pad_ops = {
.enum_mbus_code = bru_enum_mbus_code,
.enum_frame_size = bru_enum_frame_size,
.get_fmt = bru_get_format,
.set_fmt = bru_set_format,
.get_selection = bru_get_selection,
.set_selection = bru_set_selection,
};
static struct v4l2_subdev_ops bru_ops = {
.video = &bru_video_ops,
.pad = &bru_pad_ops,
static const struct vsp1_entity_operations bru_entity_ops = {
.configure = bru_configure,
};
/* -----------------------------------------------------------------------------
@ -405,7 +379,6 @@ static struct v4l2_subdev_ops bru_ops = {
struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
{
struct v4l2_subdev *subdev;
struct vsp1_bru *bru;
int ret;
@ -413,31 +386,21 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
if (bru == NULL)
return ERR_PTR(-ENOMEM);
bru->entity.ops = &bru_entity_ops;
bru->entity.type = VSP1_ENTITY_BRU;
ret = vsp1_entity_init(vsp1, &bru->entity,
vsp1->info->num_bru_inputs + 1);
ret = vsp1_entity_init(vsp1, &bru->entity, "bru",
vsp1->info->num_bru_inputs + 1, &bru_ops);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &bru->entity.subdev;
v4l2_subdev_init(subdev, &bru_ops);
subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s bru",
dev_name(vsp1->dev));
v4l2_set_subdevdata(subdev, bru);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&bru->ctrls, 1);
v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR,
0, 0xffffff, 1, 0);
bru->bgcolor = 0;
bru->entity.subdev.ctrl_handler = &bru->ctrls;
if (bru->ctrls.error) {

View file

@ -31,8 +31,9 @@ struct vsp1_bru {
struct {
struct vsp1_rwpf *rpf;
struct v4l2_rect compose;
} inputs[VSP1_MAX_RPF];
u32 bgcolor;
};
static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev)

View file

@ -18,139 +18,406 @@
#include "vsp1.h"
#include "vsp1_dl.h"
#include "vsp1_pipe.h"
/*
* Global resources
*
* - Display-related interrupts (can be used for vblank evasion ?)
* - Display-list enable
* - Header-less for WPF0
* - DL swap
*/
#define VSP1_DL_BODY_SIZE (2 * 4 * 256)
#define VSP1_DL_NUM_ENTRIES 256
#define VSP1_DL_NUM_LISTS 3
#define VSP1_DLH_INT_ENABLE (1 << 1)
#define VSP1_DLH_AUTO_START (1 << 0)
struct vsp1_dl_header_list {
u32 num_bytes;
u32 addr;
} __attribute__((__packed__));
struct vsp1_dl_header {
u32 num_lists;
struct vsp1_dl_header_list lists[8];
u32 next_header;
u32 flags;
} __attribute__((__packed__));
struct vsp1_dl_entry {
u32 addr;
u32 data;
} __attribute__((__packed__));
struct vsp1_dl_list {
size_t size;
int reg_count;
/**
* struct vsp1_dl_body - Display list body
* @list: entry in the display list list of bodies
* @vsp1: the VSP1 device
* @entries: array of entries
* @dma: DMA address of the entries
* @size: size of the DMA memory in bytes
* @num_entries: number of stored entries
*/
struct vsp1_dl_body {
struct list_head list;
struct vsp1_device *vsp1;
bool in_use;
struct vsp1_dl_entry *body;
struct vsp1_dl_entry *entries;
dma_addr_t dma;
size_t size;
unsigned int num_entries;
};
/**
* struct vsp1_dl - Display List manager
* struct vsp1_dl_list - Display list
* @list: entry in the display list manager lists
* @dlm: the display list manager
* @header: display list header, NULL for headerless lists
* @dma: DMA address for the header
* @body0: first display list body
* @fragments: list of extra display list bodies
*/
struct vsp1_dl_list {
struct list_head list;
struct vsp1_dl_manager *dlm;
struct vsp1_dl_header *header;
dma_addr_t dma;
struct vsp1_dl_body body0;
struct list_head fragments;
};
enum vsp1_dl_mode {
VSP1_DL_MODE_HEADER,
VSP1_DL_MODE_HEADERLESS,
};
/**
* struct vsp1_dl_manager - Display List manager
* @index: index of the related WPF
* @mode: display list operation mode (header or headerless)
* @vsp1: the VSP1 device
* @lock: protects the active, queued and pending lists
* @lists.all: array of all allocate display lists
* @lists.active: list currently being processed (loaded) by hardware
* @lists.queued: list queued to the hardware (written to the DL registers)
* @lists.pending: list waiting to be queued to the hardware
* @lists.write: list being written to by software
* @free: array of all free display lists
* @active: list currently being processed (loaded) by hardware
* @queued: list queued to the hardware (written to the DL registers)
* @pending: list waiting to be queued to the hardware
*/
struct vsp1_dl {
struct vsp1_dl_manager {
unsigned int index;
enum vsp1_dl_mode mode;
struct vsp1_device *vsp1;
spinlock_t lock;
size_t size;
dma_addr_t dma;
void *mem;
struct {
struct vsp1_dl_list all[VSP1_DL_NUM_LISTS];
struct vsp1_dl_list *active;
struct vsp1_dl_list *queued;
struct vsp1_dl_list *pending;
struct vsp1_dl_list *write;
} lists;
struct list_head free;
struct vsp1_dl_list *active;
struct vsp1_dl_list *queued;
struct vsp1_dl_list *pending;
};
/* -----------------------------------------------------------------------------
* Display List Body Management
*/
/*
* Initialize a display list body object and allocate DMA memory for the body
* data. The display list body object is expected to have been initialized to
* 0 when allocated.
*/
static int vsp1_dl_body_init(struct vsp1_device *vsp1,
struct vsp1_dl_body *dlb, unsigned int num_entries,
size_t extra_size)
{
size_t size = num_entries * sizeof(*dlb->entries) + extra_size;
dlb->vsp1 = vsp1;
dlb->size = size;
dlb->entries = dma_alloc_wc(vsp1->dev, dlb->size, &dlb->dma,
GFP_KERNEL);
if (!dlb->entries)
return -ENOMEM;
return 0;
}
/*
* Cleanup a display list body and free allocated DMA memory allocated.
*/
static void vsp1_dl_body_cleanup(struct vsp1_dl_body *dlb)
{
dma_free_wc(dlb->vsp1->dev, dlb->size, dlb->entries, dlb->dma);
}
/**
* vsp1_dl_fragment_alloc - Allocate a display list fragment
* @vsp1: The VSP1 device
* @num_entries: The maximum number of entries that the fragment can contain
*
* Allocate a display list fragment with enough memory to contain the requested
* number of entries.
*
* Return a pointer to a fragment on success or NULL if memory can't be
* allocated.
*/
struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1,
unsigned int num_entries)
{
struct vsp1_dl_body *dlb;
int ret;
dlb = kzalloc(sizeof(*dlb), GFP_KERNEL);
if (!dlb)
return NULL;
ret = vsp1_dl_body_init(vsp1, dlb, num_entries, 0);
if (ret < 0) {
kfree(dlb);
return NULL;
}
return dlb;
}
/**
* vsp1_dl_fragment_free - Free a display list fragment
* @dlb: The fragment
*
* Free the given display list fragment and the associated DMA memory.
*
* Fragments must only be freed explicitly if they are not added to a display
* list, as the display list will take ownership of them and free them
* otherwise. Manual free typically happens at cleanup time for fragments that
* have been allocated but not used.
*
* Passing a NULL pointer to this function is safe, in that case no operation
* will be performed.
*/
void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb)
{
if (!dlb)
return;
vsp1_dl_body_cleanup(dlb);
kfree(dlb);
}
/**
* vsp1_dl_fragment_write - Write a register to a display list fragment
* @dlb: The fragment
* @reg: The register address
* @data: The register value
*
* Write the given register and value to the display list fragment. The maximum
* number of entries that can be written in a fragment is specified when the
* fragment is allocated by vsp1_dl_fragment_alloc().
*/
void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
dlb->entries[dlb->num_entries].addr = reg;
dlb->entries[dlb->num_entries].data = data;
dlb->num_entries++;
}
/* -----------------------------------------------------------------------------
* Display List Transaction Management
*/
static void vsp1_dl_free_list(struct vsp1_dl_list *list)
static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
{
if (!list)
struct vsp1_dl_list *dl;
size_t header_size;
int ret;
dl = kzalloc(sizeof(*dl), GFP_KERNEL);
if (!dl)
return NULL;
INIT_LIST_HEAD(&dl->fragments);
dl->dlm = dlm;
/* Initialize the display list body and allocate DMA memory for the body
* and the optional header. Both are allocated together to avoid memory
* fragmentation, with the header located right after the body in
* memory.
*/
header_size = dlm->mode == VSP1_DL_MODE_HEADER
? ALIGN(sizeof(struct vsp1_dl_header), 8)
: 0;
ret = vsp1_dl_body_init(dlm->vsp1, &dl->body0, VSP1_DL_NUM_ENTRIES,
header_size);
if (ret < 0) {
kfree(dl);
return NULL;
}
if (dlm->mode == VSP1_DL_MODE_HEADER) {
size_t header_offset = VSP1_DL_NUM_ENTRIES
* sizeof(*dl->body0.entries);
dl->header = ((void *)dl->body0.entries) + header_offset;
dl->dma = dl->body0.dma + header_offset;
memset(dl->header, 0, sizeof(*dl->header));
dl->header->lists[0].addr = dl->body0.dma;
dl->header->flags = VSP1_DLH_INT_ENABLE;
}
return dl;
}
static void vsp1_dl_list_free_fragments(struct vsp1_dl_list *dl)
{
struct vsp1_dl_body *dlb, *next;
list_for_each_entry_safe(dlb, next, &dl->fragments, list) {
list_del(&dlb->list);
vsp1_dl_body_cleanup(dlb);
kfree(dlb);
}
}
static void vsp1_dl_list_free(struct vsp1_dl_list *dl)
{
vsp1_dl_body_cleanup(&dl->body0);
vsp1_dl_list_free_fragments(dl);
kfree(dl);
}
/**
* vsp1_dl_list_get - Get a free display list
* @dlm: The display list manager
*
* Get a display list from the pool of free lists and return it.
*
* This function must be called without the display list manager lock held.
*/
struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm)
{
struct vsp1_dl_list *dl = NULL;
unsigned long flags;
spin_lock_irqsave(&dlm->lock, flags);
if (!list_empty(&dlm->free)) {
dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list);
list_del(&dl->list);
}
spin_unlock_irqrestore(&dlm->lock, flags);
return dl;
}
/* This function must be called with the display list manager lock held.*/
static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
{
if (!dl)
return;
list->in_use = false;
vsp1_dl_list_free_fragments(dl);
dl->body0.num_entries = 0;
list_add_tail(&dl->list, &dl->dlm->free);
}
void vsp1_dl_reset(struct vsp1_dl *dl)
/**
* vsp1_dl_list_put - Release a display list
* @dl: The display list
*
* Release the display list and return it to the pool of free lists.
*
* Passing a NULL pointer to this function is safe, in that case no operation
* will be performed.
*/
void vsp1_dl_list_put(struct vsp1_dl_list *dl)
{
unsigned int i;
dl->lists.active = NULL;
dl->lists.queued = NULL;
dl->lists.pending = NULL;
dl->lists.write = NULL;
for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i)
dl->lists.all[i].in_use = false;
}
void vsp1_dl_begin(struct vsp1_dl *dl)
{
struct vsp1_dl_list *list = NULL;
unsigned long flags;
unsigned int i;
spin_lock_irqsave(&dl->lock, flags);
if (!dl)
return;
for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) {
if (!dl->lists.all[i].in_use) {
list = &dl->lists.all[i];
break;
}
}
if (!list) {
list = dl->lists.pending;
dl->lists.pending = NULL;
}
spin_unlock_irqrestore(&dl->lock, flags);
dl->lists.write = list;
list->in_use = true;
list->reg_count = 0;
spin_lock_irqsave(&dl->dlm->lock, flags);
__vsp1_dl_list_put(dl);
spin_unlock_irqrestore(&dl->dlm->lock, flags);
}
void vsp1_dl_add(struct vsp1_entity *e, u32 reg, u32 data)
/**
* vsp1_dl_list_write - Write a register to the display list
* @dl: The display list
* @reg: The register address
* @data: The register value
*
* Write the given register and value to the display list. Up to 256 registers
* can be written per display list.
*/
void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data)
{
struct vsp1_pipeline *pipe = to_vsp1_pipeline(&e->subdev.entity);
struct vsp1_dl *dl = pipe->dl;
struct vsp1_dl_list *list = dl->lists.write;
list->body[list->reg_count].addr = reg;
list->body[list->reg_count].data = data;
list->reg_count++;
vsp1_dl_fragment_write(&dl->body0, reg, data);
}
void vsp1_dl_commit(struct vsp1_dl *dl)
/**
* vsp1_dl_list_add_fragment - Add a fragment to the display list
* @dl: The display list
* @dlb: The fragment
*
* Add a display list body as a fragment to a display list. Registers contained
* in fragments are processed after registers contained in the main display
* list, in the order in which fragments are added.
*
* Adding a fragment to a display list passes ownership of the fragment to the
* list. The caller must not touch the fragment after this call, and must not
* free it explicitly with vsp1_dl_fragment_free().
*
* Fragments are only usable for display lists in header mode. Attempt to
* add a fragment to a header-less display list will return an error.
*/
int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
{
struct vsp1_device *vsp1 = dl->vsp1;
struct vsp1_dl_list *list;
/* Multi-body lists are only available in header mode. */
if (dl->dlm->mode != VSP1_DL_MODE_HEADER)
return -EINVAL;
list_add_tail(&dlb->list, &dl->fragments);
return 0;
}
void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
{
struct vsp1_dl_manager *dlm = dl->dlm;
struct vsp1_device *vsp1 = dlm->vsp1;
unsigned long flags;
bool update;
list = dl->lists.write;
dl->lists.write = NULL;
spin_lock_irqsave(&dlm->lock, flags);
spin_lock_irqsave(&dl->lock, flags);
if (dl->dlm->mode == VSP1_DL_MODE_HEADER) {
struct vsp1_dl_header_list *hdr = dl->header->lists;
struct vsp1_dl_body *dlb;
unsigned int num_lists = 0;
/* Fill the header with the display list bodies addresses and
* sizes. The address of the first body has already been filled
* when the display list was allocated.
*
* In header mode the caller guarantees that the hardware is
* idle at this point.
*/
hdr->num_bytes = dl->body0.num_entries
* sizeof(*dl->header->lists);
list_for_each_entry(dlb, &dl->fragments, list) {
num_lists++;
hdr++;
hdr->addr = dlb->dma;
hdr->num_bytes = dlb->num_entries
* sizeof(*dl->header->lists);
}
dl->header->num_lists = num_lists;
vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma);
dlm->active = dl;
goto done;
}
/* Once the UPD bit has been set the hardware can start processing the
* display list at any time and we can't touch the address and size
@ -159,8 +426,8 @@ void vsp1_dl_commit(struct vsp1_dl *dl)
*/
update = !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE) & VI6_DL_BODY_SIZE_UPD);
if (update) {
vsp1_dl_free_list(dl->lists.pending);
dl->lists.pending = list;
__vsp1_dl_list_put(dlm->pending);
dlm->pending = dl;
goto done;
}
@ -168,42 +435,51 @@ void vsp1_dl_commit(struct vsp1_dl *dl)
* The UPD bit will be cleared by the device when the display list is
* processed.
*/
vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), list->dma);
vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0.dma);
vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD |
(list->reg_count * 8));
(dl->body0.num_entries * sizeof(*dl->header->lists)));
vsp1_dl_free_list(dl->lists.queued);
dl->lists.queued = list;
__vsp1_dl_list_put(dlm->queued);
dlm->queued = dl;
done:
spin_unlock_irqrestore(&dl->lock, flags);
spin_unlock_irqrestore(&dlm->lock, flags);
}
/* -----------------------------------------------------------------------------
* Interrupt Handling
* Display List Manager
*/
void vsp1_dl_irq_display_start(struct vsp1_dl *dl)
/* Interrupt Handling */
void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
{
spin_lock(&dl->lock);
spin_lock(&dlm->lock);
/* The display start interrupt signals the end of the display list
* processing by the device. The active display list, if any, won't be
* accessed anymore and can be reused.
*/
if (dl->lists.active) {
vsp1_dl_free_list(dl->lists.active);
dl->lists.active = NULL;
}
__vsp1_dl_list_put(dlm->active);
dlm->active = NULL;
spin_unlock(&dl->lock);
spin_unlock(&dlm->lock);
}
void vsp1_dl_irq_frame_end(struct vsp1_dl *dl)
void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
{
struct vsp1_device *vsp1 = dl->vsp1;
struct vsp1_device *vsp1 = dlm->vsp1;
spin_lock(&dl->lock);
spin_lock(&dlm->lock);
__vsp1_dl_list_put(dlm->active);
dlm->active = NULL;
/* Header mode is used for mem-to-mem pipelines only. We don't need to
* perform any operation as there can't be any new display list queued
* in that case.
*/
if (dlm->mode == VSP1_DL_MODE_HEADER)
goto done;
/* The UPD bit set indicates that the commit operation raced with the
* interrupt and occurred after the frame end event and UPD clear but
@ -216,42 +492,39 @@ void vsp1_dl_irq_frame_end(struct vsp1_dl *dl)
/* The device starts processing the queued display list right after the
* frame end interrupt. The display list thus becomes active.
*/
if (dl->lists.queued) {
WARN_ON(dl->lists.active);
dl->lists.active = dl->lists.queued;
dl->lists.queued = NULL;
if (dlm->queued) {
dlm->active = dlm->queued;
dlm->queued = NULL;
}
/* Now that the UPD bit has been cleared we can queue the next display
* list to the hardware if one has been prepared.
*/
if (dl->lists.pending) {
struct vsp1_dl_list *list = dl->lists.pending;
if (dlm->pending) {
struct vsp1_dl_list *dl = dlm->pending;
vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), list->dma);
vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0.dma);
vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD |
(list->reg_count * 8));
(dl->body0.num_entries *
sizeof(*dl->header->lists)));
dl->lists.queued = list;
dl->lists.pending = NULL;
dlm->queued = dl;
dlm->pending = NULL;
}
done:
spin_unlock(&dl->lock);
spin_unlock(&dlm->lock);
}
/* -----------------------------------------------------------------------------
* Hardware Setup
*/
void vsp1_dl_setup(struct vsp1_device *vsp1)
/* Hardware Setup */
void vsp1_dlm_setup(struct vsp1_device *vsp1)
{
u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT)
| VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0
| VI6_DL_CTRL_DLE;
/* The DRM pipeline operates with header-less display lists in
* Continuous Frame Mode.
/* The DRM pipeline operates with display lists in Continuous Frame
* Mode, all other pipelines use manual start.
*/
if (vsp1->drm)
ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0;
@ -260,46 +533,64 @@ void vsp1_dl_setup(struct vsp1_device *vsp1)
vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS);
}
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1)
void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
{
struct vsp1_dl *dl;
unsigned long flags;
spin_lock_irqsave(&dlm->lock, flags);
__vsp1_dl_list_put(dlm->active);
__vsp1_dl_list_put(dlm->queued);
__vsp1_dl_list_put(dlm->pending);
spin_unlock_irqrestore(&dlm->lock, flags);
dlm->active = NULL;
dlm->queued = NULL;
dlm->pending = NULL;
}
struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
unsigned int index,
unsigned int prealloc)
{
struct vsp1_dl_manager *dlm;
unsigned int i;
dl = kzalloc(sizeof(*dl), GFP_KERNEL);
if (!dl)
dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL);
if (!dlm)
return NULL;
spin_lock_init(&dl->lock);
dlm->index = index;
dlm->mode = index == 0 && !vsp1->info->uapi
? VSP1_DL_MODE_HEADERLESS : VSP1_DL_MODE_HEADER;
dlm->vsp1 = vsp1;
dl->vsp1 = vsp1;
dl->size = VSP1_DL_BODY_SIZE * ARRAY_SIZE(dl->lists.all);
spin_lock_init(&dlm->lock);
INIT_LIST_HEAD(&dlm->free);
dl->mem = dma_alloc_wc(vsp1->dev, dl->size, &dl->dma,
GFP_KERNEL);
if (!dl->mem) {
kfree(dl);
return NULL;
for (i = 0; i < prealloc; ++i) {
struct vsp1_dl_list *dl;
dl = vsp1_dl_list_alloc(dlm);
if (!dl)
return NULL;
list_add_tail(&dl->list, &dlm->free);
}
for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) {
struct vsp1_dl_list *list = &dl->lists.all[i];
list->size = VSP1_DL_BODY_SIZE;
list->reg_count = 0;
list->in_use = false;
list->dma = dl->dma + VSP1_DL_BODY_SIZE * i;
list->body = dl->mem + VSP1_DL_BODY_SIZE * i;
}
return dl;
return dlm;
}
void vsp1_dl_destroy(struct vsp1_dl *dl)
void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm)
{
dma_free_wc(dl->vsp1->dev, dl->size, dl->mem, dl->dma);
kfree(dl);
struct vsp1_dl_list *dl, *next;
if (!dlm)
return;
list_for_each_entry_safe(dl, next, &dlm->free, list) {
list_del(&dl->list);
vsp1_dl_list_free(dl);
}
}

View file

@ -13,30 +13,33 @@
#ifndef __VSP1_DL_H__
#define __VSP1_DL_H__
#include "vsp1_entity.h"
#include <linux/types.h>
struct vsp1_device;
struct vsp1_dl;
struct vsp1_dl_fragment;
struct vsp1_dl_list;
struct vsp1_dl_manager;
struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1);
void vsp1_dl_destroy(struct vsp1_dl *dl);
void vsp1_dlm_setup(struct vsp1_device *vsp1);
void vsp1_dl_setup(struct vsp1_device *vsp1);
struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
unsigned int index,
unsigned int prealloc);
void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
void vsp1_dl_reset(struct vsp1_dl *dl);
void vsp1_dl_begin(struct vsp1_dl *dl);
void vsp1_dl_add(struct vsp1_entity *e, u32 reg, u32 data);
void vsp1_dl_commit(struct vsp1_dl *dl);
struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm);
void vsp1_dl_list_put(struct vsp1_dl_list *dl);
void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data);
void vsp1_dl_list_commit(struct vsp1_dl_list *dl);
void vsp1_dl_irq_display_start(struct vsp1_dl *dl);
void vsp1_dl_irq_frame_end(struct vsp1_dl *dl);
static inline void vsp1_dl_mod_write(struct vsp1_entity *e, u32 reg, u32 data)
{
if (e->vsp1->use_dl)
vsp1_dl_add(e, reg, data);
else
vsp1_write(e->vsp1, reg, data);
}
struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1,
unsigned int num_entries);
void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb);
void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb);
#endif /* __VSP1_DL_H__ */

View file

@ -13,10 +13,10 @@
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/vsp1.h>
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include <media/vsp1.h>
#include "vsp1.h"
#include "vsp1_bru.h"
@ -26,18 +26,14 @@
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
/* -----------------------------------------------------------------------------
* Runtime Handling
* Interrupt Handling
*/
static void vsp1_drm_pipeline_frame_end(struct vsp1_pipeline *pipe)
void vsp1_drm_display_start(struct vsp1_device *vsp1)
{
unsigned long flags;
spin_lock_irqsave(&pipe->irqlock, flags);
if (pipe->num_inputs)
vsp1_pipeline_run(pipe);
spin_unlock_irqrestore(&pipe->irqlock, flags);
vsp1_dlm_irq_display_start(vsp1->drm->pipe.output->dlm);
}
/* -----------------------------------------------------------------------------
@ -97,12 +93,14 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width,
media_entity_pipeline_stop(&pipe->output->entity.subdev.entity);
for (i = 0; i < bru->entity.source_pad; ++i) {
vsp1->drm->inputs[i].enabled = false;
bru->inputs[i].rpf = NULL;
pipe->inputs[i] = NULL;
}
pipe->num_inputs = 0;
vsp1_dlm_reset(pipe->output->dlm);
vsp1_device_put(vsp1);
dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__);
@ -110,8 +108,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width,
return 0;
}
vsp1_dl_reset(vsp1->drm->dl);
/* Configure the format at the BRU sinks and propagate it through the
* pipeline.
*/
@ -222,16 +218,11 @@ void vsp1_du_atomic_begin(struct device *dev)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
unsigned long flags;
spin_lock_irqsave(&pipe->irqlock, flags);
vsp1->drm->num_inputs = pipe->num_inputs;
spin_unlock_irqrestore(&pipe->irqlock, flags);
/* Prepare the display list. */
vsp1_dl_begin(vsp1->drm->dl);
pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
@ -244,10 +235,13 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
* @mem: DMA addresses of the memory buffers (one per plane)
* @src: the source crop rectangle for the RPF
* @dst: the destination compose rectangle for the BRU input
* @alpha: global alpha value for the input
* @zpos: the Z-order position of the input
*
* Configure the VSP to perform composition of the image referenced by @mem
* through RPF @rpf_index, using the @src crop rectangle and the @dst
* composition rectangle. The Z-order is fixed with RPF 0 at the bottom.
* composition rectangle. The Z-order is configurable with higher @zpos values
* displayed on top.
*
* Image format as stored in memory is expressed as a V4L2 @pixelformat value.
* As a special case, setting the pixel format to 0 will disable the RPF. The
@ -265,25 +259,17 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
*
* This function isn't reentrant, the caller needs to serialize calls.
*
* TODO: Implement Z-order control by decoupling the RPF index from the BRU
* input index.
*
* Return 0 on success or a negative error code on failure.
*/
int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
u32 pixelformat, unsigned int pitch,
dma_addr_t mem[2], const struct v4l2_rect *src,
const struct v4l2_rect *dst)
int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index,
u32 pixelformat, unsigned int pitch,
dma_addr_t mem[2], const struct v4l2_rect *src,
const struct v4l2_rect *dst, unsigned int alpha,
unsigned int zpos)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
const struct vsp1_format_info *fmtinfo;
struct v4l2_subdev_selection sel;
struct v4l2_subdev_format format;
struct vsp1_rwpf_memory memory;
struct vsp1_rwpf *rpf;
unsigned long flags;
int ret;
if (rpf_index >= vsp1->info->rpf_count)
return -EINVAL;
@ -294,31 +280,20 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
dev_dbg(vsp1->dev, "%s: RPF%u: disable requested\n", __func__,
rpf_index);
spin_lock_irqsave(&pipe->irqlock, flags);
if (pipe->inputs[rpf_index]) {
/* Remove the RPF from the pipeline if it was previously
* enabled.
*/
vsp1->bru->inputs[rpf_index].rpf = NULL;
pipe->inputs[rpf_index] = NULL;
pipe->num_inputs--;
}
spin_unlock_irqrestore(&pipe->irqlock, flags);
vsp1->drm->inputs[rpf_index].enabled = false;
return 0;
}
dev_dbg(vsp1->dev,
"%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad }\n",
"%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad } zpos %u\n",
__func__, rpf_index,
src->left, src->top, src->width, src->height,
dst->left, dst->top, dst->width, dst->height,
pixelformat, pitch, &mem[0], &mem[1]);
pixelformat, pitch, &mem[0], &mem[1], zpos);
/* Set the stride at the RPF input. */
/* Store the format, stride, memory buffer address, crop and compose
* rectangles and Z-order position and for the input.
*/
fmtinfo = vsp1_get_format_info(pixelformat);
if (!fmtinfo) {
dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n",
@ -330,16 +305,40 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
rpf->format.num_planes = fmtinfo->planes;
rpf->format.plane_fmt[0].bytesperline = pitch;
rpf->format.plane_fmt[1].bytesperline = pitch;
rpf->alpha = alpha;
rpf->mem.addr[0] = mem[0];
rpf->mem.addr[1] = mem[1];
rpf->mem.addr[2] = 0;
vsp1->drm->inputs[rpf_index].crop = *src;
vsp1->drm->inputs[rpf_index].compose = *dst;
vsp1->drm->inputs[rpf_index].zpos = zpos;
vsp1->drm->inputs[rpf_index].enabled = true;
return 0;
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_update_ext);
static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
struct vsp1_rwpf *rpf, unsigned int bru_input)
{
struct v4l2_subdev_selection sel;
struct v4l2_subdev_format format;
const struct v4l2_rect *crop;
int ret;
/* Configure the format on the RPF sink pad and propagate it up to the
* BRU sink pad.
*/
crop = &vsp1->drm->inputs[rpf->entity.index].crop;
memset(&format, 0, sizeof(format));
format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
format.pad = RWPF_PAD_SINK;
format.format.width = src->width + src->left;
format.format.height = src->height + src->top;
format.format.code = fmtinfo->mbus;
format.format.width = crop->width + crop->left;
format.format.height = crop->height + crop->top;
format.format.code = rpf->fmtinfo->mbus;
format.format.field = V4L2_FIELD_NONE;
ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
@ -356,7 +355,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.pad = RWPF_PAD_SINK;
sel.target = V4L2_SEL_TGT_CROP;
sel.r = *src;
sel.r = *crop;
ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
&sel);
@ -391,7 +390,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
return ret;
/* BRU sink, propagate the format from the RPF source. */
format.pad = rpf->entity.index;
format.pad = bru_input;
ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_fmt, NULL,
&format);
@ -402,9 +401,9 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
__func__, format.format.width, format.format.height,
format.format.code, format.pad);
sel.pad = rpf->entity.index;
sel.pad = bru_input;
sel.target = V4L2_SEL_TGT_COMPOSE;
sel.r = *dst;
sel.r = vsp1->drm->inputs[rpf->entity.index].compose;
ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_selection,
NULL, &sel);
@ -416,33 +415,13 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
__func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
sel.pad);
/* Store the compose rectangle coordinates in the RPF. */
rpf->location.left = dst->left;
rpf->location.top = dst->top;
/* Set the memory buffer address. */
memory.num_planes = fmtinfo->planes;
memory.addr[0] = mem[0];
memory.addr[1] = mem[1];
rpf->ops->set_memory(rpf, &memory);
spin_lock_irqsave(&pipe->irqlock, flags);
/* If the RPF was previously stopped set the BRU input to the RPF and
* store the RPF in the pipeline inputs array.
*/
if (!pipe->inputs[rpf->entity.index]) {
vsp1->bru->inputs[rpf_index].rpf = rpf;
pipe->inputs[rpf->entity.index] = rpf;
pipe->num_inputs++;
}
spin_unlock_irqrestore(&pipe->irqlock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_update);
static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf)
{
return vsp1->drm->inputs[rpf->entity.index].zpos;
}
/**
* vsp1_du_atomic_flush - Commit an atomic update
@ -452,51 +431,96 @@ void vsp1_du_atomic_flush(struct device *dev)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, };
struct vsp1_entity *entity;
unsigned long flags;
bool stop = false;
unsigned int i;
int ret;
/* Count the number of enabled inputs and sort them by Z-order. */
pipe->num_inputs = 0;
for (i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *rpf = vsp1->rpf[i];
unsigned int j;
if (!vsp1->drm->inputs[i].enabled) {
pipe->inputs[i] = NULL;
continue;
}
pipe->inputs[i] = rpf;
/* Insert the RPF in the sorted RPFs array. */
for (j = pipe->num_inputs++; j > 0; --j) {
if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf))
break;
inputs[j] = inputs[j-1];
}
inputs[j] = rpf;
}
/* Setup the RPF input pipeline for every enabled input. */
for (i = 0; i < vsp1->info->num_bru_inputs; ++i) {
struct vsp1_rwpf *rpf = inputs[i];
if (!rpf) {
vsp1->bru->inputs[i].rpf = NULL;
continue;
}
vsp1->bru->inputs[i].rpf = rpf;
rpf->bru_input = i;
rpf->entity.sink_pad = i;
dev_dbg(vsp1->dev, "%s: connecting RPF.%u to BRU:%u\n",
__func__, rpf->entity.index, i);
ret = vsp1_du_setup_rpf_pipe(vsp1, rpf, i);
if (ret < 0)
dev_err(vsp1->dev,
"%s: failed to setup RPF.%u\n",
__func__, rpf->entity.index);
}
/* Configure all entities in the pipeline. */
list_for_each_entry(entity, &pipe->entities, list_pipe) {
/* Disconnect unused RPFs from the pipeline. */
if (entity->type == VSP1_ENTITY_RPF) {
struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
if (!pipe->inputs[rpf->entity.index]) {
vsp1_mod_write(entity, entity->route->reg,
VI6_DPR_NODE_UNUSED);
vsp1_dl_list_write(pipe->dl, entity->route->reg,
VI6_DPR_NODE_UNUSED);
continue;
}
}
vsp1_entity_route_setup(entity);
vsp1_entity_route_setup(entity, pipe->dl);
ret = v4l2_subdev_call(&entity->subdev, video,
s_stream, 1);
if (ret < 0) {
dev_err(vsp1->dev,
"DRM pipeline start failure on entity %s\n",
entity->subdev.name);
return;
}
if (entity->ops->configure)
entity->ops->configure(entity, pipe, pipe->dl);
/* The memory buffer address must be applied after configuring
* the RPF to make sure the crop offset are computed.
*/
if (entity->type == VSP1_ENTITY_RPF)
vsp1_rwpf_set_memory(to_rwpf(&entity->subdev),
pipe->dl);
}
vsp1_dl_commit(vsp1->drm->dl);
spin_lock_irqsave(&pipe->irqlock, flags);
vsp1_dl_list_commit(pipe->dl);
pipe->dl = NULL;
/* Start or stop the pipeline if needed. */
if (!vsp1->drm->num_inputs && pipe->num_inputs) {
vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0);
vsp1_write(vsp1, VI6_DISP_IRQ_ENB, VI6_DISP_IRQ_ENB_DSTE);
spin_lock_irqsave(&pipe->irqlock, flags);
vsp1_pipeline_run(pipe);
spin_unlock_irqrestore(&pipe->irqlock, flags);
} else if (vsp1->drm->num_inputs && !pipe->num_inputs) {
stop = true;
}
spin_unlock_irqrestore(&pipe->irqlock, flags);
if (stop) {
vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0);
vsp1_pipeline_stop(pipe);
}
@ -562,14 +586,9 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
if (!vsp1->drm)
return -ENOMEM;
vsp1->drm->dl = vsp1_dl_create(vsp1);
if (!vsp1->drm->dl)
return -ENOMEM;
pipe = &vsp1->drm->pipe;
vsp1_pipeline_init(pipe);
pipe->frame_end = vsp1_drm_pipeline_frame_end;
/* The DRM pipeline is static, add entities manually. */
for (i = 0; i < vsp1->info->rpf_count; ++i) {
@ -586,12 +605,9 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
pipe->lif = &vsp1->lif->entity;
pipe->output = vsp1->wpf[0];
pipe->dl = vsp1->drm->dl;
return 0;
}
void vsp1_drm_cleanup(struct vsp1_device *vsp1)
{
vsp1_dl_destroy(vsp1->drm->dl);
}

View file

@ -13,37 +13,32 @@
#ifndef __VSP1_DRM_H__
#define __VSP1_DRM_H__
#include "vsp1_pipe.h"
#include <linux/videodev2.h>
struct vsp1_dl;
#include "vsp1_pipe.h"
/**
* vsp1_drm - State for the API exposed to the DRM driver
* @dl: display list for DRM pipeline operation
* @pipe: the VSP1 pipeline used for display
* @num_inputs: number of active pipeline inputs at the beginning of an update
* @update: the pipeline configuration has been updated
* @planes: source crop rectangle, destination compose rectangle and z-order
* position for every input
*/
struct vsp1_drm {
struct vsp1_dl *dl;
struct vsp1_pipeline pipe;
unsigned int num_inputs;
bool update;
struct {
bool enabled;
struct v4l2_rect crop;
struct v4l2_rect compose;
unsigned int zpos;
} inputs[VSP1_MAX_RPF];
};
int vsp1_drm_init(struct vsp1_device *vsp1);
void vsp1_drm_cleanup(struct vsp1_device *vsp1);
int vsp1_drm_create_links(struct vsp1_device *vsp1);
int vsp1_du_init(struct device *dev);
int vsp1_du_setup_lif(struct device *dev, unsigned int width,
unsigned int height);
void vsp1_du_atomic_begin(struct device *dev);
int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
u32 pixelformat, unsigned int pitch,
dma_addr_t mem[2], const struct v4l2_rect *src,
const struct v4l2_rect *dst);
void vsp1_du_atomic_flush(struct device *dev);
void vsp1_drm_display_start(struct vsp1_device *vsp1);
#endif /* __VSP1_DRM_H__ */

View file

@ -30,6 +30,7 @@
#include "vsp1_hsit.h"
#include "vsp1_lif.h"
#include "vsp1_lut.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
#include "vsp1_sru.h"
#include "vsp1_uds.h"
@ -49,17 +50,15 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data)
for (i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
struct vsp1_pipeline *pipe;
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);
vsp1_pipeline_frame_end(wpf->pipe);
ret = IRQ_HANDLED;
}
}
@ -68,14 +67,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data)
vsp1_write(vsp1, VI6_DISP_IRQ_STA, ~status & VI6_DISP_IRQ_STA_DST);
if (status & VI6_DISP_IRQ_STA_DST) {
struct vsp1_rwpf *wpf = vsp1->wpf[0];
struct vsp1_pipeline *pipe;
if (wpf) {
pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
vsp1_pipeline_display_start(pipe);
}
vsp1_drm_display_start(vsp1);
ret = IRQ_HANDLED;
}
@ -387,13 +379,10 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
/* Register subdev nodes if the userspace API is enabled or initialize
* the DRM pipeline otherwise.
*/
if (vsp1->info->uapi) {
vsp1->use_dl = false;
if (vsp1->info->uapi)
ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
} else {
vsp1->use_dl = true;
else
ret = vsp1_drm_init(vsp1);
}
if (ret < 0)
goto done;
@ -465,8 +454,7 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
(VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
if (vsp1->use_dl)
vsp1_dl_setup(vsp1);
vsp1_dlm_setup(vsp1);
return 0;
}
@ -570,6 +558,7 @@ static const struct dev_pm_ops vsp1_pm_ops = {
static const struct vsp1_device_info vsp1_device_infos[] = {
{
.version = VI6_IP_VERSION_MODEL_VSPS_H2,
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
.rpf_count = 5,
.uds_count = 3,
@ -578,6 +567,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPR_H2,
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_SRU,
.rpf_count = 5,
.uds_count = 1,
@ -586,6 +576,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPD_GEN2,
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
.rpf_count = 4,
.uds_count = 1,
@ -594,6 +585,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPS_M2,
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
.rpf_count = 5,
.uds_count = 3,
@ -602,6 +594,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPI_GEN3,
.gen = 3,
.features = VSP1_HAS_LUT | VSP1_HAS_SRU,
.rpf_count = 1,
.uds_count = 1,
@ -609,6 +602,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPBD_GEN3,
.gen = 3,
.features = VSP1_HAS_BRU,
.rpf_count = 5,
.wpf_count = 1,
@ -616,6 +610,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPBC_GEN3,
.gen = 3,
.features = VSP1_HAS_BRU | VSP1_HAS_LUT,
.rpf_count = 5,
.wpf_count = 1,
@ -623,7 +618,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPD_GEN3,
.features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
.gen = 3,
.features = VSP1_HAS_BRU | VSP1_HAS_LIF,
.rpf_count = 5,
.wpf_count = 2,
.num_bru_inputs = 5,

View file

@ -19,46 +19,11 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_dl.h"
#include "vsp1_entity.h"
bool vsp1_entity_is_streaming(struct vsp1_entity *entity)
{
unsigned long flags;
bool streaming;
spin_lock_irqsave(&entity->lock, flags);
streaming = entity->streaming;
spin_unlock_irqrestore(&entity->lock, flags);
return streaming;
}
int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&entity->lock, flags);
entity->streaming = streaming;
spin_unlock_irqrestore(&entity->lock, flags);
if (!streaming)
return 0;
if (!entity->vsp1->info->uapi || !entity->subdev.ctrl_handler)
return 0;
ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler);
if (ret < 0) {
spin_lock_irqsave(&entity->lock, flags);
entity->streaming = false;
spin_unlock_irqrestore(&entity->lock, flags);
}
return ret;
}
void vsp1_entity_route_setup(struct vsp1_entity *source)
void vsp1_entity_route_setup(struct vsp1_entity *source,
struct vsp1_dl_list *dl)
{
struct vsp1_entity *sink;
@ -66,40 +31,74 @@ void vsp1_entity_route_setup(struct vsp1_entity *source)
return;
sink = container_of(source->sink, struct vsp1_entity, subdev.entity);
vsp1_mod_write(source, source->route->reg,
sink->route->inputs[source->sink_pad]);
vsp1_dl_list_write(dl, source->route->reg,
sink->route->inputs[source->sink_pad]);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
struct v4l2_mbus_framefmt *
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
/**
* vsp1_entity_get_pad_config - Get the pad configuration for an entity
* @entity: the entity
* @cfg: the TRY pad configuration
* @which: configuration selector (ACTIVE or TRY)
*
* Return the pad configuration requested by the which argument. The TRY
* configuration is passed explicitly to the function through the cfg argument
* and simply returned when requested. The ACTIVE configuration comes from the
* entity structure.
*/
struct v4l2_subdev_pad_config *
vsp1_entity_get_pad_config(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad, u32 which)
enum v4l2_subdev_format_whence which)
{
switch (which) {
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad);
case V4L2_SUBDEV_FORMAT_ACTIVE:
return &entity->formats[pad];
return entity->config;
case V4L2_SUBDEV_FORMAT_TRY:
default:
return NULL;
return cfg;
}
}
/**
* vsp1_entity_get_pad_format - Get a pad format from storage for an entity
* @entity: the entity
* @cfg: the configuration storage
* @pad: the pad number
*
* Return the format stored in the given configuration for an entity's pad. The
* configuration can be an ACTIVE or TRY configuration.
*/
struct v4l2_mbus_framefmt *
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad)
{
return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad);
}
struct v4l2_rect *
vsp1_entity_get_pad_compose(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad)
{
return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad);
}
/*
* vsp1_entity_init_formats - Initialize formats on all pads
* vsp1_entity_init_cfg - Initialize formats on all pads
* @subdev: V4L2 subdevice
* @cfg: V4L2 subdev pad configuration
*
* Initialize all pad formats with default values. If cfg is not NULL, try
* formats are initialized on the file handle. Otherwise active formats are
* initialized on the device.
* Initialize all pad formats with default values in the given pad config. This
* function can be used as a handler for the subdev pad::init_cfg operation.
*/
void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg)
int vsp1_entity_init_cfg(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg)
{
struct v4l2_subdev_format format;
unsigned int pad;
@ -113,19 +112,132 @@ void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
v4l2_subdev_call(subdev, pad, set_fmt, cfg, &format);
}
}
static int vsp1_entity_open(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh)
{
vsp1_entity_init_formats(subdev, fh->pad);
return 0;
}
const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = {
.open = vsp1_entity_open,
};
/*
* vsp1_subdev_get_pad_format - Subdev pad get_fmt handler
* @subdev: V4L2 subdevice
* @cfg: V4L2 subdev pad configuration
* @fmt: V4L2 subdev format
*
* This function implements the subdev get_fmt pad operation. It can be used as
* a direct drop-in for the operation handler.
*/
int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_entity *entity = to_vsp1_entity(subdev);
struct v4l2_subdev_pad_config *config;
config = vsp1_entity_get_pad_config(entity, cfg, fmt->which);
if (!config)
return -EINVAL;
fmt->format = *vsp1_entity_get_pad_format(entity, config, fmt->pad);
return 0;
}
/*
* vsp1_subdev_enum_mbus_code - Subdev pad enum_mbus_code handler
* @subdev: V4L2 subdevice
* @cfg: V4L2 subdev pad configuration
* @code: Media bus code enumeration
* @codes: Array of supported media bus codes
* @ncodes: Number of supported media bus codes
*
* This function implements the subdev enum_mbus_code pad operation for entities
* that do not support format conversion. It enumerates the given supported
* media bus codes on the sink pad and reports a source pad format identical to
* the sink pad.
*/
int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code,
const unsigned int *codes, unsigned int ncodes)
{
struct vsp1_entity *entity = to_vsp1_entity(subdev);
if (code->pad == 0) {
if (code->index >= ncodes)
return -EINVAL;
code->code = codes[code->index];
} else {
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
/* The entity can't perform format conversion, the sink format
* is always identical to the source format.
*/
if (code->index)
return -EINVAL;
config = vsp1_entity_get_pad_config(entity, cfg, code->which);
if (!config)
return -EINVAL;
format = vsp1_entity_get_pad_format(entity, config, 0);
code->code = format->code;
}
return 0;
}
/*
* vsp1_subdev_enum_frame_size - Subdev pad enum_frame_size handler
* @subdev: V4L2 subdevice
* @cfg: V4L2 subdev pad configuration
* @fse: Frame size enumeration
* @min_width: Minimum image width
* @min_height: Minimum image height
* @max_width: Maximum image width
* @max_height: Maximum image height
*
* This function implements the subdev enum_frame_size pad operation for
* entities that do not support scaling or cropping. It reports the given
* minimum and maximum frame width and height on the sink pad, and a fixed
* source pad size identical to the sink pad.
*/
int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse,
unsigned int min_width, unsigned int min_height,
unsigned int max_width, unsigned int max_height)
{
struct vsp1_entity *entity = to_vsp1_entity(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
config = vsp1_entity_get_pad_config(entity, cfg, fse->which);
if (!config)
return -EINVAL;
format = vsp1_entity_get_pad_format(entity, config, fse->pad);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == 0) {
fse->min_width = min_width;
fse->max_width = max_width;
fse->min_height = min_height;
fse->max_height = 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;
}
/* -----------------------------------------------------------------------------
* Media Operations
@ -171,11 +283,11 @@ static const struct vsp1_route vsp1_routes[] = {
{ VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } },
{ VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } },
{ VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } },
{ VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { VI6_DPR_NODE_RPF(0), } },
{ VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { VI6_DPR_NODE_RPF(1), } },
{ VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { VI6_DPR_NODE_RPF(2), } },
{ VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { VI6_DPR_NODE_RPF(3), } },
{ VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { VI6_DPR_NODE_RPF(4), } },
{ VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { 0, } },
{ VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { 0, } },
{ VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { 0, } },
{ VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { 0, } },
{ VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { 0, } },
{ VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } },
{ VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } },
{ VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } },
@ -187,9 +299,12 @@ static const struct vsp1_route vsp1_routes[] = {
};
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
unsigned int num_pads)
const char *name, unsigned int num_pads,
const struct v4l2_subdev_ops *ops)
{
struct v4l2_subdev *subdev;
unsigned int i;
int ret;
for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) {
if (vsp1_routes[i].type == entity->type &&
@ -202,37 +317,56 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
if (i == ARRAY_SIZE(vsp1_routes))
return -EINVAL;
spin_lock_init(&entity->lock);
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;
/* Allocate and initialize pads. */
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_pads_init(&entity->subdev.entity, num_pads,
entity->pads);
ret = media_entity_pads_init(&entity->subdev.entity, num_pads,
entity->pads);
if (ret < 0)
return ret;
/* Initialize the V4L2 subdev. */
subdev = &entity->subdev;
v4l2_subdev_init(subdev, ops);
subdev->entity.ops = &vsp1->media_ops;
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(subdev->name, sizeof(subdev->name), "%s %s",
dev_name(vsp1->dev), name);
vsp1_entity_init_cfg(subdev, NULL);
/* Allocate the pad configuration to store formats and selection
* rectangles.
*/
entity->config = v4l2_subdev_alloc_pad_config(&entity->subdev);
if (entity->config == NULL) {
media_entity_cleanup(&entity->subdev.entity);
return -ENOMEM;
}
return 0;
}
void vsp1_entity_destroy(struct vsp1_entity *entity)
{
if (entity->ops && entity->ops->destroy)
entity->ops->destroy(entity);
if (entity->subdev.ctrl_handler)
v4l2_ctrl_handler_free(entity->subdev.ctrl_handler);
v4l2_subdev_free_pad_config(entity->config);
media_entity_cleanup(&entity->subdev.entity);
}

View file

@ -19,6 +19,8 @@
#include <media/v4l2-subdev.h>
struct vsp1_device;
struct vsp1_dl_list;
struct vsp1_pipeline;
enum vsp1_entity_type {
VSP1_ENTITY_BRU,
@ -53,9 +55,27 @@ struct vsp1_route {
unsigned int inputs[VSP1_ENTITY_MAX_INPUTS];
};
/**
* struct vsp1_entity_operations - Entity operations
* @destroy: Destroy the entity.
* @set_memory: Setup memory buffer access. This operation applies the settings
* stored in the rwpf mem field to the display list. Valid for RPF
* and WPF only.
* @configure: Setup the hardware based on the entity state (pipeline, formats,
* selection rectangles, ...)
*/
struct vsp1_entity_operations {
void (*destroy)(struct vsp1_entity *);
void (*set_memory)(struct vsp1_entity *, struct vsp1_dl_list *dl);
void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *,
struct vsp1_dl_list *);
};
struct vsp1_entity {
struct vsp1_device *vsp1;
const struct vsp1_entity_operations *ops;
enum vsp1_entity_type type;
unsigned int index;
const struct vsp1_route *route;
@ -70,10 +90,7 @@ struct vsp1_entity {
unsigned int sink_pad;
struct v4l2_subdev subdev;
struct v4l2_mbus_framefmt *formats;
spinlock_t lock; /* Protects the streaming field */
bool streaming;
struct v4l2_subdev_pad_config *config;
};
static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
@ -82,7 +99,8 @@ static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
}
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
unsigned int num_pads);
const char *name, unsigned int num_pads,
const struct v4l2_subdev_ops *ops);
void vsp1_entity_destroy(struct vsp1_entity *entity);
extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops;
@ -91,16 +109,35 @@ int vsp1_entity_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags);
struct v4l2_subdev_pad_config *
vsp1_entity_get_pad_config(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
enum v4l2_subdev_format_whence which);
struct v4l2_mbus_framefmt *
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad, u32 which);
void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg);
unsigned int pad);
struct v4l2_rect *
vsp1_entity_get_pad_compose(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad);
int vsp1_entity_init_cfg(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg);
bool vsp1_entity_is_streaming(struct vsp1_entity *entity);
int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming);
void vsp1_entity_route_setup(struct vsp1_entity *source,
struct vsp1_dl_list *dl);
void vsp1_entity_route_setup(struct vsp1_entity *source);
int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt);
int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code,
const unsigned int *codes, unsigned int ncodes);
int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse,
unsigned int min_w, unsigned int min_h,
unsigned int max_w, unsigned int max_h);
#endif /* __VSP1_ENTITY_H__ */

View file

@ -17,6 +17,7 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_dl.h"
#include "vsp1_hsit.h"
#define HSIT_MIN_SIZE 4U
@ -26,32 +27,14 @@
* Device Access
*/
static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data)
static inline void vsp1_hsit_write(struct vsp1_hsit *hsit,
struct vsp1_dl_list *dl, u32 reg, u32 data)
{
vsp1_write(hsit->entity.vsp1, reg, data);
vsp1_dl_list_write(dl, reg, data);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int hsit_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_hsit *hsit = to_hsit(subdev);
if (!enable)
return 0;
if (hsit->inverse)
vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
else
vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
* V4L2 Subdevice Operations
*/
static int hsit_enum_mbus_code(struct v4l2_subdev *subdev,
@ -76,43 +59,9 @@ static int hsit_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_hsit *hsit = to_hsit(subdev);
struct v4l2_mbus_framefmt *format;
format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fse->pad,
fse->which);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == HSIT_PAD_SINK) {
fse->min_width = HSIT_MIN_SIZE;
fse->max_width = HSIT_MAX_SIZE;
fse->min_height = HSIT_MIN_SIZE;
fse->max_height = HSIT_MAX_SIZE;
} 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;
}
static int hsit_get_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_hsit *hsit = to_hsit(subdev);
fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad,
fmt->which);
return 0;
return vsp1_subdev_enum_frame_size(subdev, cfg, fse, HSIT_MIN_SIZE,
HSIT_MIN_SIZE, HSIT_MAX_SIZE,
HSIT_MAX_SIZE);
}
static int hsit_set_format(struct v4l2_subdev *subdev,
@ -120,10 +69,14 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_format *fmt)
{
struct vsp1_hsit *hsit = to_hsit(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad,
fmt->which);
config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fmt->which);
if (!config)
return -EINVAL;
format = vsp1_entity_get_pad_format(&hsit->entity, config, fmt->pad);
if (fmt->pad == HSIT_PAD_SOURCE) {
/* The HST and HSI output format code and resolution can't be
@ -145,8 +98,8 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
fmt->format = *format;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&hsit->entity, cfg, HSIT_PAD_SOURCE,
fmt->which);
format = vsp1_entity_get_pad_format(&hsit->entity, config,
HSIT_PAD_SOURCE);
*format = fmt->format;
format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32
: MEDIA_BUS_FMT_AHSV8888_1X32;
@ -154,33 +107,44 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops hsit_video_ops = {
.s_stream = hsit_s_stream,
};
static struct v4l2_subdev_pad_ops hsit_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = hsit_enum_mbus_code,
.enum_frame_size = hsit_enum_frame_size,
.get_fmt = hsit_get_format,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = hsit_set_format,
};
static struct v4l2_subdev_ops hsit_ops = {
.video = &hsit_video_ops,
.pad = &hsit_pad_ops,
};
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
static void hsit_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
{
struct vsp1_hsit *hsit = to_hsit(&entity->subdev);
if (hsit->inverse)
vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
else
vsp1_hsit_write(hsit, dl, VI6_HST_CTRL, VI6_HST_CTRL_EN);
}
static const struct vsp1_entity_operations hsit_entity_ops = {
.configure = hsit_configure,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse)
{
struct v4l2_subdev *subdev;
struct vsp1_hsit *hsit;
int ret;
@ -190,27 +154,17 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse)
hsit->inverse = inverse;
hsit->entity.ops = &hsit_entity_ops;
if (inverse)
hsit->entity.type = VSP1_ENTITY_HSI;
else
hsit->entity.type = VSP1_ENTITY_HST;
ret = vsp1_entity_init(vsp1, &hsit->entity, 2);
ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst", 2,
&hsit_ops);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &hsit->entity.subdev;
v4l2_subdev_init(subdev, &hsit_ops);
subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s %s",
dev_name(vsp1->dev), inverse ? "hsi" : "hst");
v4l2_set_subdevdata(subdev, hsit);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
return hsit;
}

View file

@ -17,55 +17,24 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_dl.h"
#include "vsp1_lif.h"
#define LIF_MIN_SIZE 2U
#define LIF_MAX_SIZE 2048U
#define LIF_MAX_SIZE 8190U
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data)
static inline void vsp1_lif_write(struct vsp1_lif *lif, struct vsp1_dl_list *dl,
u32 reg, u32 data)
{
vsp1_mod_write(&lif->entity, reg, data);
vsp1_dl_list_write(dl, 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_write(lif->entity.vsp1, 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
* V4L2 Subdevice Operations
*/
static int lif_enum_mbus_code(struct v4l2_subdev *subdev,
@ -76,82 +45,38 @@ static int lif_enum_mbus_code(struct v4l2_subdev *subdev,
MEDIA_BUS_FMT_ARGB8888_1X32,
MEDIA_BUS_FMT_AYUV8_1X32,
};
struct vsp1_lif *lif = to_lif(subdev);
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 = vsp1_entity_get_pad_format(&lif->entity, cfg,
LIF_PAD_SINK, code->which);
code->code = format->code;
}
return 0;
return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
ARRAY_SIZE(codes));
}
static int lif_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
return vsp1_subdev_enum_frame_size(subdev, cfg, fse, LIF_MIN_SIZE,
LIF_MIN_SIZE, LIF_MAX_SIZE,
LIF_MAX_SIZE);
}
static int lif_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lif *lif = to_lif(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SINK,
fse->which);
if (fse->index || fse->code != format->code)
config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which);
if (!config)
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_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lif *lif = to_lif(subdev);
fmt->format = *vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad,
fmt->which);
return 0;
}
static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
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 != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad,
fmt->which);
format = vsp1_entity_get_pad_format(&lif->entity, config, fmt->pad);
if (fmt->pad == LIF_PAD_SOURCE) {
/* The LIF source format is always identical to its sink
@ -172,40 +97,64 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con
fmt->format = *format;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SOURCE,
fmt->which);
format = vsp1_entity_get_pad_format(&lif->entity, config,
LIF_PAD_SOURCE);
*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 = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = lif_enum_mbus_code,
.enum_frame_size = lif_enum_frame_size,
.get_fmt = lif_get_format,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = lif_set_format,
};
static struct v4l2_subdev_ops lif_ops = {
.video = &lif_video_ops,
.pad = &lif_pad_ops,
};
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
static void lif_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
{
const struct v4l2_mbus_framefmt *format;
struct vsp1_lif *lif = to_lif(&entity->subdev);
unsigned int hbth = 1300;
unsigned int obth = 400;
unsigned int lbth = 200;
format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config,
LIF_PAD_SOURCE);
obth = min(obth, (format->width + 1) / 2 * format->height - 4);
vsp1_lif_write(lif, dl, VI6_LIF_CSBTH,
(hbth << VI6_LIF_CSBTH_HBTH_SHIFT) |
(lbth << VI6_LIF_CSBTH_LBTH_SHIFT));
vsp1_lif_write(lif, dl, 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);
}
static const struct vsp1_entity_operations lif_entity_ops = {
.configure = lif_configure,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
{
struct v4l2_subdev *subdev;
struct vsp1_lif *lif;
int ret;
@ -213,24 +162,12 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
if (lif == NULL)
return ERR_PTR(-ENOMEM);
lif->entity.ops = &lif_entity_ops;
lif->entity.type = VSP1_ENTITY_LIF;
ret = vsp1_entity_init(vsp1, &lif->entity, 2);
ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops);
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

@ -18,6 +18,7 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_dl.h"
#include "vsp1_lut.h"
#define LUT_MIN_SIZE 4U
@ -27,19 +28,35 @@
* Device Access
*/
static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data)
static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl,
u32 reg, u32 data)
{
vsp1_write(lut->entity.vsp1, reg, data);
vsp1_dl_list_write(dl, reg, data);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static void lut_configure(struct vsp1_lut *lut, struct vsp1_lut_config *config)
static int lut_set_table(struct vsp1_lut *lut, struct vsp1_lut_config *config)
{
memcpy_toio(lut->entity.vsp1->mmio + VI6_LUT_TABLE, config->lut,
sizeof(config->lut));
struct vsp1_dl_body *dlb;
unsigned int i;
dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, ARRAY_SIZE(config->lut));
if (!dlb)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(config->lut); ++i)
vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i,
config->lut[i]);
mutex_lock(&lut->lock);
swap(lut->lut, dlb);
mutex_unlock(&lut->lock);
vsp1_dl_fragment_free(dlb);
return 0;
}
static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg)
@ -48,30 +65,13 @@ static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg)
switch (cmd) {
case VIDIOC_VSP1_LUT_CONFIG:
lut_configure(lut, arg);
return 0;
return lut_set_table(lut, arg);
default:
return -ENOIOCTLCMD;
}
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Video Operations
*/
static int lut_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_lut *lut = to_lut(subdev);
if (!enable)
return 0;
vsp1_lut_write(lut, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
@ -85,85 +85,39 @@ static int lut_enum_mbus_code(struct v4l2_subdev *subdev,
MEDIA_BUS_FMT_AHSV8888_1X32,
MEDIA_BUS_FMT_AYUV8_1X32,
};
struct vsp1_lut *lut = to_lut(subdev);
struct v4l2_mbus_framefmt *format;
if (code->pad == LUT_PAD_SINK) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
/* The LUT can't perform format conversion, the sink format is
* always identical to the source format.
*/
if (code->index)
return -EINVAL;
format = vsp1_entity_get_pad_format(&lut->entity, cfg,
LUT_PAD_SINK, code->which);
code->code = format->code;
}
return 0;
return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
ARRAY_SIZE(codes));
}
static int lut_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
return vsp1_subdev_enum_frame_size(subdev, cfg, fse, LUT_MIN_SIZE,
LUT_MIN_SIZE, LUT_MAX_SIZE,
LUT_MAX_SIZE);
}
static int lut_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lut *lut = to_lut(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
format = vsp1_entity_get_pad_format(&lut->entity, cfg,
fse->pad, fse->which);
if (fse->index || fse->code != format->code)
config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which);
if (!config)
return -EINVAL;
if (fse->pad == LUT_PAD_SINK) {
fse->min_width = LUT_MIN_SIZE;
fse->max_width = LUT_MAX_SIZE;
fse->min_height = LUT_MIN_SIZE;
fse->max_height = LUT_MAX_SIZE;
} 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;
}
static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lut *lut = to_lut(subdev);
fmt->format = *vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad,
fmt->which);
return 0;
}
static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lut *lut = to_lut(subdev);
struct v4l2_mbus_framefmt *format;
/* Default to YUV if the requested format is not supported. */
if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad,
fmt->which);
format = vsp1_entity_get_pad_format(&lut->entity, config, fmt->pad);
if (fmt->pad == LUT_PAD_SOURCE) {
/* The LUT output format can't be modified. */
@ -171,6 +125,7 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con
return 0;
}
format->code = fmt->format.code;
format->width = clamp_t(unsigned int, fmt->format.width,
LUT_MIN_SIZE, LUT_MAX_SIZE);
format->height = clamp_t(unsigned int, fmt->format.height,
@ -181,8 +136,8 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con
fmt->format = *format;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&lut->entity, cfg, LUT_PAD_SOURCE,
fmt->which);
format = vsp1_entity_get_pad_format(&lut->entity, config,
LUT_PAD_SOURCE);
*format = fmt->format;
return 0;
@ -196,30 +151,49 @@ static struct v4l2_subdev_core_ops lut_core_ops = {
.ioctl = lut_ioctl,
};
static struct v4l2_subdev_video_ops lut_video_ops = {
.s_stream = lut_s_stream,
};
static struct v4l2_subdev_pad_ops lut_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = lut_enum_mbus_code,
.enum_frame_size = lut_enum_frame_size,
.get_fmt = lut_get_format,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = lut_set_format,
};
static struct v4l2_subdev_ops lut_ops = {
.core = &lut_core_ops,
.video = &lut_video_ops,
.pad = &lut_pad_ops,
};
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
static void lut_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
{
struct vsp1_lut *lut = to_lut(&entity->subdev);
vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
mutex_lock(&lut->lock);
if (lut->lut) {
vsp1_dl_list_add_fragment(dl, lut->lut);
lut->lut = NULL;
}
mutex_unlock(&lut->lock);
}
static const struct vsp1_entity_operations lut_entity_ops = {
.configure = lut_configure,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
{
struct v4l2_subdev *subdev;
struct vsp1_lut *lut;
int ret;
@ -227,24 +201,12 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
if (lut == NULL)
return ERR_PTR(-ENOMEM);
lut->entity.ops = &lut_entity_ops;
lut->entity.type = VSP1_ENTITY_LUT;
ret = vsp1_entity_init(vsp1, &lut->entity, 2);
ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &lut->entity.subdev;
v4l2_subdev_init(subdev, &lut_ops);
subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s lut",
dev_name(vsp1->dev));
v4l2_set_subdevdata(subdev, lut);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
return lut;
}

View file

@ -13,6 +13,8 @@
#ifndef __VSP1_LUT_H__
#define __VSP1_LUT_H__
#include <linux/mutex.h>
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
@ -25,7 +27,9 @@ struct vsp1_device;
struct vsp1_lut {
struct vsp1_entity entity;
u32 lut[256];
struct mutex lock;
struct vsp1_dl_body *lut;
};
static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev)

View file

@ -43,7 +43,7 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
{ V4L2_PIX_FMT_XRGB444, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
1, { 16, 0, 0 }, false, false, 1, 1, true },
1, { 16, 0, 0 }, false, false, 1, 1, false },
{ V4L2_PIX_FMT_ARGB555, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
@ -172,14 +172,18 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
bru->inputs[i].rpf = NULL;
}
for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i)
for (i = 0; i < pipe->num_inputs; ++i) {
pipe->inputs[i]->pipe = NULL;
pipe->inputs[i] = NULL;
}
pipe->output->pipe = NULL;
pipe->output = NULL;
INIT_LIST_HEAD(&pipe->entities);
pipe->state = VSP1_PIPELINE_STOPPED;
pipe->buffers_ready = 0;
pipe->num_inputs = 0;
pipe->output = NULL;
pipe->bru = NULL;
pipe->lif = NULL;
pipe->uds = NULL;
@ -190,11 +194,13 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe)
mutex_init(&pipe->lock);
spin_lock_init(&pipe->irqlock);
init_waitqueue_head(&pipe->wq);
kref_init(&pipe->kref);
INIT_LIST_HEAD(&pipe->entities);
pipe->state = VSP1_PIPELINE_STOPPED;
}
/* Must be called with the pipe irqlock held. */
void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
@ -226,7 +232,7 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
unsigned long flags;
int ret;
if (pipe->dl) {
if (pipe->lif) {
/* When using display lists in continuous frame mode the only
* way to stop the pipeline is to reset the hardware.
*/
@ -253,10 +259,10 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
if (entity->route && entity->route->reg)
vsp1_write(entity->vsp1, entity->route->reg,
VI6_DPR_NODE_UNUSED);
v4l2_subdev_call(&entity->subdev, video, s_stream, 0);
}
v4l2_subdev_call(&pipe->output->entity.subdev, video, s_stream, 0);
return ret;
}
@ -271,50 +277,15 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
return pipe->buffers_ready == mask;
}
void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe)
{
if (pipe->dl)
vsp1_dl_irq_display_start(pipe->dl);
}
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
{
enum vsp1_pipeline_state state;
unsigned long flags;
if (pipe == NULL)
return;
if (pipe->dl)
vsp1_dl_irq_frame_end(pipe->dl);
vsp1_dlm_irq_frame_end(pipe->output->dlm);
/* Signal frame end to the pipeline handler. */
pipe->frame_end(pipe);
spin_lock_irqsave(&pipe->irqlock, flags);
state = pipe->state;
/* When using display lists in continuous frame mode the pipeline is
* automatically restarted by the hardware.
*/
if (!pipe->dl)
pipe->state = VSP1_PIPELINE_STOPPED;
/* If a stop has been requested, mark the pipeline as stopped and
* return.
*/
if (state == VSP1_PIPELINE_STOPPING) {
wake_up(&pipe->wq);
goto done;
}
/* Restart the pipeline if ready. */
if (vsp1_pipeline_ready(pipe))
vsp1_pipeline_run(pipe);
done:
spin_unlock_irqrestore(&pipe->irqlock, flags);
if (pipe->frame_end)
pipe->frame_end(pipe);
}
/*
@ -324,9 +295,13 @@ done:
* to be scaled, we disable alpha scaling when the UDS input has a fixed alpha
* value. The UDS then outputs a fixed alpha value which needs to be programmed
* from the input RPF alpha.
*
* This function can only be called from a subdev s_stream handler as it
* requires a valid display list context.
*/
void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
struct vsp1_entity *input,
struct vsp1_dl_list *dl,
unsigned int alpha)
{
struct vsp1_entity *entity;
@ -349,7 +324,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
if (entity->type == VSP1_ENTITY_UDS) {
struct vsp1_uds *uds = to_uds(&entity->subdev);
vsp1_uds_set_alpha(uds, alpha);
vsp1_uds_set_alpha(uds, dl, alpha);
break;
}
@ -375,7 +350,7 @@ void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
if (wpf == NULL)
continue;
pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
pipe = wpf->pipe;
if (pipe == NULL)
continue;
@ -392,7 +367,7 @@ void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
if (wpf == NULL)
continue;
pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
pipe = wpf->pipe;
if (pipe == NULL)
continue;
@ -416,7 +391,7 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1)
if (wpf == NULL)
continue;
pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
pipe = wpf->pipe;
if (pipe == NULL)
continue;

View file

@ -13,13 +13,14 @@
#ifndef __VSP1_PIPE_H__
#define __VSP1_PIPE_H__
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <media/media-entity.h>
struct vsp1_dl;
struct vsp1_dl_list;
struct vsp1_rwpf;
/*
@ -63,7 +64,7 @@ enum vsp1_pipeline_state {
* @wq: work queue to wait for state change completion
* @frame_end: frame end interrupt handler
* @lock: protects the pipeline use count and stream count
* @use_count: number of video nodes using the pipeline
* @kref: pipeline reference count
* @stream_count: number of streaming video nodes
* @buffers_ready: bitmask of RPFs and WPFs with at least one buffer available
* @num_inputs: number of RPFs
@ -86,7 +87,7 @@ struct vsp1_pipeline {
void (*frame_end)(struct vsp1_pipeline *pipe);
struct mutex lock;
unsigned int use_count;
struct kref kref;
unsigned int stream_count;
unsigned int buffers_ready;
@ -100,17 +101,9 @@ struct vsp1_pipeline {
struct list_head entities;
struct vsp1_dl *dl;
struct vsp1_dl_list *dl;
};
static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e)
{
if (likely(e->pipe))
return container_of(e->pipe, struct vsp1_pipeline, pipe);
else
return NULL;
}
void vsp1_pipeline_reset(struct vsp1_pipeline *pipe);
void vsp1_pipeline_init(struct vsp1_pipeline *pipe);
@ -119,11 +112,11 @@ bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe);
int vsp1_pipeline_stop(struct vsp1_pipeline *pipe);
bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe);
void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe);
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
struct vsp1_entity *input,
struct vsp1_dl_list *dl,
unsigned int alpha);
void vsp1_pipelines_suspend(struct vsp1_device *vsp1);

View file

@ -217,6 +217,16 @@
#define VI6_RPF_SRCM_ADDR_C1 0x0344
#define VI6_RPF_SRCM_ADDR_AI 0x0348
#define VI6_RPF_MULT_ALPHA 0x036c
#define VI6_RPF_MULT_ALPHA_A_MMD_NONE (0 << 12)
#define VI6_RPF_MULT_ALPHA_A_MMD_RATIO (1 << 12)
#define VI6_RPF_MULT_ALPHA_P_MMD_NONE (0 << 8)
#define VI6_RPF_MULT_ALPHA_P_MMD_RATIO (1 << 8)
#define VI6_RPF_MULT_ALPHA_P_MMD_IMAGE (2 << 8)
#define VI6_RPF_MULT_ALPHA_P_MMD_BOTH (3 << 8)
#define VI6_RPF_MULT_ALPHA_RATIO_MASK (0xff < 0)
#define VI6_RPF_MULT_ALPHA_RATIO_SHIFT 0
/* -----------------------------------------------------------------------------
* WPF Control Registers
*/

View file

@ -16,6 +16,8 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_dl.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
@ -26,64 +28,50 @@
* Device Access
*/
static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data)
static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf,
struct vsp1_dl_list *dl, u32 reg, u32 data)
{
vsp1_mod_write(&rpf->entity, reg + rpf->entity.index * VI6_RPF_OFFSET,
data);
vsp1_dl_list_write(dl, reg + rpf->entity.index * VI6_RPF_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* Controls
* V4L2 Subdevice Operations
*/
static int rpf_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_rwpf *rpf =
container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
struct vsp1_pipeline *pipe;
if (!vsp1_entity_is_streaming(&rpf->entity))
return 0;
switch (ctrl->id) {
case V4L2_CID_ALPHA_COMPONENT:
vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
pipe = to_vsp1_pipeline(&rpf->entity.subdev.entity);
vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, ctrl->val);
break;
}
return 0;
}
static const struct v4l2_ctrl_ops rpf_ctrl_ops = {
.s_ctrl = rpf_s_ctrl,
static struct v4l2_subdev_ops rpf_ops = {
.pad = &vsp1_rwpf_pad_ops,
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
* VSP1 Entity Operations
*/
static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl)
{
struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity);
struct vsp1_rwpf *rpf = to_rwpf(subdev);
struct vsp1_device *vsp1 = rpf->entity.vsp1;
struct vsp1_rwpf *rpf = entity_to_rwpf(entity);
vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y,
rpf->mem.addr[0] + rpf->offsets[0]);
vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0,
rpf->mem.addr[1] + rpf->offsets[1]);
vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1,
rpf->mem.addr[2] + rpf->offsets[1]);
}
static void rpf_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
{
struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
const struct v4l2_pix_format_mplane *format = &rpf->format;
const struct v4l2_rect *crop = &rpf->crop;
const struct v4l2_mbus_framefmt *source_format;
const struct v4l2_mbus_framefmt *sink_format;
const struct v4l2_rect *crop;
unsigned int left = 0;
unsigned int top = 0;
u32 pstride;
u32 infmt;
int ret;
ret = vsp1_entity_set_streaming(&rpf->entity, enable);
if (ret < 0)
return ret;
if (!enable)
return 0;
/* Source size, stride and crop offsets.
*
@ -91,10 +79,12 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
* left corner in the plane buffer. Only two offsets are needed, as
* planes 2 and 3 always have identical strides.
*/
vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE,
crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config);
vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE,
(crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
(crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE,
vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE,
(crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
(crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
@ -103,26 +93,25 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
pstride = format->plane_fmt[0].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
rpf->buf_addr[0] + rpf->offsets[0]);
if (format->num_planes > 1) {
rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline
+ crop->left * fmtinfo->bpp[1] / 8;
pstride |= format->plane_fmt[1].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0,
rpf->buf_addr[1] + rpf->offsets[1]);
if (format->num_planes > 2)
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1,
rpf->buf_addr[2] + rpf->offsets[1]);
} else {
rpf->offsets[1] = 0;
}
vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride);
vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride);
/* Format */
sink_format = vsp1_entity_get_pad_format(&rpf->entity,
rpf->entity.config,
RWPF_PAD_SINK);
source_format = vsp1_entity_get_pad_format(&rpf->entity,
rpf->entity.config,
RWPF_PAD_SOURCE);
infmt = VI6_RPF_INFMT_CIPM
| (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
@ -131,88 +120,98 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
if (fmtinfo->swap_uv)
infmt |= VI6_RPF_INFMT_SPUVS;
if (rpf->entity.formats[RWPF_PAD_SINK].code !=
rpf->entity.formats[RWPF_PAD_SOURCE].code)
if (sink_format->code != source_format->code)
infmt |= VI6_RPF_INFMT_CSC;
vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);
vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap);
vsp1_rpf_write(rpf, dl, VI6_RPF_INFMT, infmt);
vsp1_rpf_write(rpf, dl, VI6_RPF_DSWAP, fmtinfo->swap);
/* Output location */
vsp1_rpf_write(rpf, VI6_RPF_LOC,
(rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) |
(rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT));
if (pipe->bru) {
const struct v4l2_rect *compose;
/* Use the alpha channel (extended to 8 bits) when available or an
* alpha value set through the V4L2_CID_ALPHA_COMPONENT control
* otherwise. Disable color keying.
compose = vsp1_entity_get_pad_compose(pipe->bru,
pipe->bru->config,
rpf->bru_input);
left = compose->left;
top = compose->top;
}
vsp1_rpf_write(rpf, dl, VI6_RPF_LOC,
(left << VI6_RPF_LOC_HCOORD_SHIFT) |
(top << VI6_RPF_LOC_VCOORD_SHIFT));
/* On Gen2 use the alpha channel (extended to 8 bits) when available or
* a fixed alpha value set through the V4L2_CID_ALPHA_COMPONENT control
* otherwise.
*
* The Gen3 RPF has extended alpha capability and can both multiply the
* alpha channel by a fixed global alpha value, and multiply the pixel
* components to convert the input to premultiplied alpha.
*
* As alpha premultiplication is available in the BRU for both Gen2 and
* Gen3 we handle it there and use the Gen3 alpha multiplier for global
* alpha multiplication only. This however prevents conversion to
* premultiplied alpha if no BRU is present in the pipeline. If that use
* case turns out to be useful we will revisit the implementation (for
* Gen3 only).
*
* We enable alpha multiplication on Gen3 using the fixed alpha value
* set through the V4L2_CID_ALPHA_COMPONENT control when the input
* contains an alpha channel. On Gen2 the global alpha is ignored in
* that case.
*
* In all cases, disable color keying.
*/
vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
(fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
: VI6_RPF_ALPH_SEL_ASEL_FIXED));
if (vsp1->info->uapi)
mutex_lock(rpf->ctrls.lock);
vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
rpf->alpha->cur.val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, rpf->alpha->cur.val);
if (vsp1->info->uapi)
mutex_unlock(rpf->ctrls.lock);
vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET,
rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
if (entity->vsp1->info->gen == 3) {
u32 mult;
if (fmtinfo->alpha) {
/* When the input contains an alpha channel enable the
* alpha multiplier. If the input is premultiplied we
* need to multiply both the alpha channel and the pixel
* components by the global alpha value to keep them
* premultiplied. Otherwise multiply the alpha channel
* only.
*/
bool premultiplied = format->flags
& V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO
| (premultiplied ?
VI6_RPF_MULT_ALPHA_P_MMD_RATIO :
VI6_RPF_MULT_ALPHA_P_MMD_NONE)
| (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT);
} else {
/* When the input doesn't contain an alpha channel the
* global alpha value is applied in the unpacking unit,
* the alpha multiplier isn't needed and must be
* disabled.
*/
mult = VI6_RPF_MULT_ALPHA_A_MMD_NONE
| VI6_RPF_MULT_ALPHA_P_MMD_NONE;
}
vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, mult);
}
vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, dl, rpf->alpha);
vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0);
vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0);
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,
.get_selection = vsp1_rwpf_get_selection,
.set_selection = vsp1_rwpf_set_selection,
};
static struct v4l2_subdev_ops rpf_ops = {
.video = &rpf_video_ops,
.pad = &rpf_pad_ops,
};
/* -----------------------------------------------------------------------------
* Video Device Operations
*/
static void rpf_set_memory(struct vsp1_rwpf *rpf, struct vsp1_rwpf_memory *mem)
{
unsigned int i;
for (i = 0; i < 3; ++i)
rpf->buf_addr[i] = mem->addr[i];
if (!vsp1_entity_is_streaming(&rpf->entity))
return;
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
mem->addr[0] + rpf->offsets[0]);
if (mem->num_planes > 1)
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0,
mem->addr[1] + rpf->offsets[1]);
if (mem->num_planes > 2)
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1,
mem->addr[2] + rpf->offsets[1]);
}
static const struct vsp1_rwpf_operations rpf_vdev_ops = {
static const struct vsp1_entity_operations rpf_entity_ops = {
.set_memory = rpf_set_memory,
.configure = rpf_configure,
};
/* -----------------------------------------------------------------------------
@ -221,51 +220,31 @@ static const struct vsp1_rwpf_operations rpf_vdev_ops = {
struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
{
struct v4l2_subdev *subdev;
struct vsp1_rwpf *rpf;
char name[6];
int ret;
rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL);
if (rpf == NULL)
return ERR_PTR(-ENOMEM);
rpf->ops = &rpf_vdev_ops;
rpf->max_width = RPF_MAX_WIDTH;
rpf->max_height = RPF_MAX_HEIGHT;
rpf->entity.ops = &rpf_entity_ops;
rpf->entity.type = VSP1_ENTITY_RPF;
rpf->entity.index = index;
ret = vsp1_entity_init(vsp1, &rpf->entity, 2);
sprintf(name, "rpf.%u", index);
ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops);
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 control handler. */
v4l2_ctrl_handler_init(&rpf->ctrls, 1);
rpf->alpha = v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops,
V4L2_CID_ALPHA_COMPONENT,
0, 255, 1, 255);
rpf->entity.subdev.ctrl_handler = &rpf->ctrls;
if (rpf->ctrls.error) {
ret = vsp1_rwpf_init_ctrls(rpf);
if (ret < 0) {
dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n",
index);
ret = rpf->ctrls.error;
goto error;
}

View file

@ -20,13 +20,20 @@
#define RWPF_MIN_WIDTH 1
#define RWPF_MIN_HEIGHT 1
struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
struct v4l2_subdev_pad_config *config)
{
return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, config,
RWPF_PAD_SINK);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
MEDIA_BUS_FMT_ARGB8888_1X32,
@ -41,75 +48,36 @@ int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
return 0;
}
int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_mbus_framefmt *format;
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fse->pad,
fse->which);
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;
}
static struct v4l2_rect *
vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *cfg, u32 which)
{
switch (which) {
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, cfg, RWPF_PAD_SINK);
case V4L2_SUBDEV_FORMAT_ACTIVE:
return &rwpf->crop;
default:
return NULL;
}
}
int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad,
fmt->which);
return 0;
return vsp1_subdev_enum_frame_size(subdev, cfg, fse, RWPF_MIN_WIDTH,
RWPF_MIN_HEIGHT, rwpf->max_width,
rwpf->max_height);
}
int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which);
if (!config)
return -EINVAL;
/* Default to YUV if the requested format is not supported. */
if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad,
fmt->which);
format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad);
if (fmt->pad == RWPF_PAD_SOURCE) {
/* The RWPF performs format conversion but can't scale, only the
@ -131,39 +99,44 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_conf
fmt->format = *format;
/* Update the sink crop rectangle. */
crop = vsp1_rwpf_get_crop(rwpf, cfg, fmt->which);
crop = vsp1_rwpf_get_crop(rwpf, config);
crop->left = 0;
crop->top = 0;
crop->width = fmt->format.width;
crop->height = fmt->format.height;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE,
fmt->which);
format = vsp1_entity_get_pad_format(&rwpf->entity, config,
RWPF_PAD_SOURCE);
*format = fmt->format;
return 0;
}
int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
/* Cropping is implemented on the sink pad. */
if (sel->pad != RWPF_PAD_SINK)
return -EINVAL;
config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which);
if (!config)
return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
sel->r = *vsp1_rwpf_get_crop(rwpf, cfg, sel->which);
sel->r = *vsp1_rwpf_get_crop(rwpf, config);
break;
case V4L2_SEL_TGT_CROP_BOUNDS:
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg,
RWPF_PAD_SINK, sel->which);
format = vsp1_entity_get_pad_format(&rwpf->entity, config,
RWPF_PAD_SINK);
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = format->width;
@ -177,11 +150,12 @@ int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
return 0;
}
int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
@ -192,11 +166,15 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which);
if (!config)
return -EINVAL;
/* Make sure the crop rectangle is entirely contained in the image. The
* WPF top and left offsets are limited to 255.
*/
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK,
sel->which);
format = vsp1_entity_get_pad_format(&rwpf->entity, config,
RWPF_PAD_SINK);
/* Restrict the crop rectangle coordinates to multiples of 2 to avoid
* shifting the color plane.
@ -219,14 +197,59 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
sel->r.height = min_t(unsigned int, sel->r.height,
format->height - sel->r.top);
crop = vsp1_rwpf_get_crop(rwpf, cfg, sel->which);
crop = vsp1_rwpf_get_crop(rwpf, config);
*crop = sel->r;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE,
sel->which);
format = vsp1_entity_get_pad_format(&rwpf->entity, config,
RWPF_PAD_SOURCE);
format->width = crop->width;
format->height = crop->height;
return 0;
}
const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = vsp1_rwpf_enum_mbus_code,
.enum_frame_size = vsp1_rwpf_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = vsp1_rwpf_set_format,
.get_selection = vsp1_rwpf_get_selection,
.set_selection = vsp1_rwpf_set_selection,
};
/* -----------------------------------------------------------------------------
* Controls
*/
static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_rwpf *rwpf =
container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
switch (ctrl->id) {
case V4L2_CID_ALPHA_COMPONENT:
rwpf->alpha = ctrl->val;
break;
}
return 0;
}
static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {
.s_ctrl = vsp1_rwpf_s_ctrl,
};
int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf)
{
rwpf->alpha = 255;
v4l2_ctrl_handler_init(&rwpf->ctrls, 1);
v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls;
return rwpf->ctrls.error;
}

View file

@ -24,42 +24,35 @@
#define RWPF_PAD_SOURCE 1
struct v4l2_ctrl;
struct vsp1_dl_manager;
struct vsp1_pipeline;
struct vsp1_rwpf;
struct vsp1_video;
struct vsp1_rwpf_memory {
unsigned int num_planes;
dma_addr_t addr[3];
unsigned int length[3];
};
struct vsp1_rwpf_operations {
void (*set_memory)(struct vsp1_rwpf *rwpf,
struct vsp1_rwpf_memory *mem);
};
struct vsp1_rwpf {
struct vsp1_entity entity;
struct v4l2_ctrl_handler ctrls;
struct v4l2_ctrl *alpha;
struct vsp1_pipeline *pipe;
struct vsp1_video *video;
const struct vsp1_rwpf_operations *ops;
unsigned int max_width;
unsigned int max_height;
struct v4l2_pix_format_mplane format;
const struct vsp1_format_info *fmtinfo;
struct {
unsigned int left;
unsigned int top;
} location;
struct v4l2_rect crop;
unsigned int bru_input;
unsigned int alpha;
unsigned int offsets[2];
dma_addr_t buf_addr[3];
struct vsp1_rwpf_memory mem;
struct vsp1_dl_manager *dlm;
};
static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
@ -67,24 +60,31 @@ static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
return container_of(subdev, struct vsp1_rwpf, entity.subdev);
}
static inline struct vsp1_rwpf *entity_to_rwpf(struct vsp1_entity *entity)
{
return container_of(entity, struct vsp1_rwpf, entity);
}
struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index);
struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index);
int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code);
int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse);
int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt);
int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt);
int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel);
int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel);
int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf);
extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops;
struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
struct v4l2_subdev_pad_config *config);
/**
* vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF
* @rwpf: the [RW]PF instance
* @dl: the display list
*
* This function applies the cached memory buffer address to the display list.
*/
static inline void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf,
struct vsp1_dl_list *dl)
{
rwpf->entity.ops->set_memory(&rwpf->entity, dl);
}
#endif /* __VSP1_RWPF_H__ */

View file

@ -17,6 +17,7 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_dl.h"
#include "vsp1_sru.h"
#define SRU_MIN_SIZE 4U
@ -26,14 +27,10 @@
* Device Access
*/
static inline u32 vsp1_sru_read(struct vsp1_sru *sru, u32 reg)
static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl,
u32 reg, u32 data)
{
return vsp1_read(sru->entity.vsp1, reg);
}
static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data)
{
vsp1_write(sru->entity.vsp1, reg, data);
vsp1_dl_list_write(dl, reg, data);
}
/* -----------------------------------------------------------------------------
@ -82,20 +79,10 @@ static int sru_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_sru *sru =
container_of(ctrl->handler, struct vsp1_sru, ctrls);
const struct vsp1_sru_param *param;
u32 value;
switch (ctrl->id) {
case V4L2_CID_VSP1_SRU_INTENSITY:
param = &vsp1_sru_params[ctrl->val - 1];
value = vsp1_sru_read(sru, VI6_SRU_CTRL0);
value &= ~(VI6_SRU_CTRL0_PARAM0_MASK |
VI6_SRU_CTRL0_PARAM1_MASK);
value |= param->ctrl0;
vsp1_sru_write(sru, VI6_SRU_CTRL0, value);
vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2);
sru->intensity = ctrl->val;
break;
}
@ -118,54 +105,7 @@ static const struct v4l2_ctrl_config sru_intensity_control = {
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int sru_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_sru *sru = to_sru(subdev);
struct v4l2_mbus_framefmt *input;
struct v4l2_mbus_framefmt *output;
u32 ctrl0;
int ret;
ret = vsp1_entity_set_streaming(&sru->entity, enable);
if (ret < 0)
return ret;
if (!enable)
return 0;
input = &sru->entity.formats[SRU_PAD_SINK];
output = &sru->entity.formats[SRU_PAD_SOURCE];
if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
| VI6_SRU_CTRL0_PARAM4;
else
ctrl0 = VI6_SRU_CTRL0_PARAM3;
if (input->width != output->width)
ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
/* Take the control handler lock to ensure that the CTRL0 value won't be
* changed behind our back by a set control operation.
*/
if (sru->entity.vsp1->info->uapi)
mutex_lock(sru->ctrls.lock);
ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0)
& (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK);
vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0);
if (sru->entity.vsp1->info->uapi)
mutex_unlock(sru->ctrls.lock);
vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
* V4L2 Subdevice Operations
*/
static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
@ -176,27 +116,9 @@ static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
MEDIA_BUS_FMT_ARGB8888_1X32,
MEDIA_BUS_FMT_AYUV8_1X32,
};
struct vsp1_sru *sru = to_sru(subdev);
struct v4l2_mbus_framefmt *format;
if (code->pad == SRU_PAD_SINK) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
/* The SRU can't perform format conversion, the sink format is
* always identical to the source format.
*/
if (code->index)
return -EINVAL;
format = vsp1_entity_get_pad_format(&sru->entity, cfg,
SRU_PAD_SINK, code->which);
code->code = format->code;
}
return 0;
return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
ARRAY_SIZE(codes));
}
static int sru_enum_frame_size(struct v4l2_subdev *subdev,
@ -204,10 +126,14 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_sru *sru = to_sru(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
format = vsp1_entity_get_pad_format(&sru->entity, cfg,
SRU_PAD_SINK, fse->which);
config = vsp1_entity_get_pad_config(&sru->entity, cfg, fse->which);
if (!config)
return -EINVAL;
format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK);
if (fse->index || fse->code != format->code)
return -EINVAL;
@ -233,20 +159,9 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev,
return 0;
}
static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_sru *sru = to_sru(subdev);
fmt->format = *vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad,
fmt->which);
return 0;
}
static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config *cfg,
unsigned int pad, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
static void sru_try_format(struct vsp1_sru *sru,
struct v4l2_subdev_pad_config *config,
unsigned int pad, struct v4l2_mbus_framefmt *fmt)
{
struct v4l2_mbus_framefmt *format;
unsigned int input_area;
@ -265,8 +180,8 @@ static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config *
case SRU_PAD_SOURCE:
/* The SRU can't perform format conversion. */
format = vsp1_entity_get_pad_format(&sru->entity, cfg,
SRU_PAD_SINK, which);
format = vsp1_entity_get_pad_format(&sru->entity, config,
SRU_PAD_SINK);
fmt->code = format->code;
/* We can upscale by 2 in both direction, but not independently.
@ -295,57 +210,94 @@ static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config *
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
static int sru_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_sru *sru = to_sru(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
sru_try_format(sru, cfg, fmt->pad, &fmt->format, fmt->which);
config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which);
if (!config)
return -EINVAL;
format = vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad,
fmt->which);
sru_try_format(sru, config, fmt->pad, &fmt->format);
format = vsp1_entity_get_pad_format(&sru->entity, config, fmt->pad);
*format = fmt->format;
if (fmt->pad == SRU_PAD_SINK) {
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&sru->entity, cfg,
SRU_PAD_SOURCE, fmt->which);
format = vsp1_entity_get_pad_format(&sru->entity, config,
SRU_PAD_SOURCE);
*format = fmt->format;
sru_try_format(sru, cfg, SRU_PAD_SOURCE, format, fmt->which);
sru_try_format(sru, config, SRU_PAD_SOURCE, format);
}
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops sru_video_ops = {
.s_stream = sru_s_stream,
};
static struct v4l2_subdev_pad_ops sru_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = sru_enum_mbus_code,
.enum_frame_size = sru_enum_frame_size,
.get_fmt = sru_get_format,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = sru_set_format,
};
static struct v4l2_subdev_ops sru_ops = {
.video = &sru_video_ops,
.pad = &sru_pad_ops,
};
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
static void sru_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
{
const struct vsp1_sru_param *param;
struct vsp1_sru *sru = to_sru(&entity->subdev);
struct v4l2_mbus_framefmt *input;
struct v4l2_mbus_framefmt *output;
u32 ctrl0;
input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
SRU_PAD_SINK);
output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
SRU_PAD_SOURCE);
if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
| VI6_SRU_CTRL0_PARAM4;
else
ctrl0 = VI6_SRU_CTRL0_PARAM3;
if (input->width != output->width)
ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
param = &vsp1_sru_params[sru->intensity - 1];
ctrl0 |= param->ctrl0;
vsp1_sru_write(sru, dl, VI6_SRU_CTRL0, ctrl0);
vsp1_sru_write(sru, dl, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2);
}
static const struct vsp1_entity_operations sru_entity_ops = {
.configure = sru_configure,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
{
struct v4l2_subdev *subdev;
struct vsp1_sru *sru;
int ret;
@ -353,29 +305,19 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
if (sru == NULL)
return ERR_PTR(-ENOMEM);
sru->entity.ops = &sru_entity_ops;
sru->entity.type = VSP1_ENTITY_SRU;
ret = vsp1_entity_init(vsp1, &sru->entity, 2);
ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &sru->entity.subdev;
v4l2_subdev_init(subdev, &sru_ops);
subdev->entity.ops = &vsp1->media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s sru",
dev_name(vsp1->dev));
v4l2_set_subdevdata(subdev, sru);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&sru->ctrls, 1);
v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
sru->intensity = 1;
sru->entity.subdev.ctrl_handler = &sru->ctrls;
if (sru->ctrls.error) {

View file

@ -28,6 +28,8 @@ struct vsp1_sru {
struct vsp1_entity entity;
struct v4l2_ctrl_handler ctrls;
unsigned int intensity;
};
static inline struct vsp1_sru *to_sru(struct v4l2_subdev *subdev)

View file

@ -17,6 +17,7 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_dl.h"
#include "vsp1_uds.h"
#define UDS_MIN_SIZE 4U
@ -29,19 +30,21 @@
* Device Access
*/
static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data)
static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
u32 reg, u32 data)
{
vsp1_write(uds->entity.vsp1,
reg + uds->entity.index * VI6_UDS_OFFSET, data);
vsp1_dl_list_write(dl, reg + uds->entity.index * VI6_UDS_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* Scaling Computation
*/
void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha)
void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
unsigned int alpha)
{
vsp1_uds_write(uds, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT);
vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL,
alpha << VI6_UDS_ALPVAL_VAL0_SHIFT);
}
/*
@ -104,60 +107,6 @@ static unsigned int uds_compute_ratio(unsigned int input, unsigned int output)
return (input - 1) * 4096 / (output - 1);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int uds_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_uds *uds = to_uds(subdev);
const struct v4l2_mbus_framefmt *output;
const struct v4l2_mbus_framefmt *input;
unsigned int hscale;
unsigned int vscale;
bool multitap;
if (!enable)
return 0;
input = &uds->entity.formats[UDS_PAD_SINK];
output = &uds->entity.formats[UDS_PAD_SOURCE];
hscale = uds_compute_ratio(input->width, output->width);
vscale = uds_compute_ratio(input->height, output->height);
dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale);
/* Multi-tap scaling can't be enabled along with alpha scaling when
* scaling down with a factor lower than or equal to 1/2 in either
* direction.
*/
if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192))
multitap = false;
else
multitap = true;
vsp1_uds_write(uds, VI6_UDS_CTRL,
(uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) |
(multitap ? VI6_UDS_CTRL_BC : 0));
vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH,
(uds_passband_width(hscale)
<< VI6_UDS_PASS_BWIDTH_H_SHIFT) |
(uds_passband_width(vscale)
<< VI6_UDS_PASS_BWIDTH_V_SHIFT));
/* Set the scaling ratios and the output size. */
vsp1_uds_write(uds, VI6_UDS_SCALE,
(hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
(vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE,
(output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
(output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
@ -170,28 +119,9 @@ static int uds_enum_mbus_code(struct v4l2_subdev *subdev,
MEDIA_BUS_FMT_ARGB8888_1X32,
MEDIA_BUS_FMT_AYUV8_1X32,
};
struct vsp1_uds *uds = to_uds(subdev);
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 = vsp1_entity_get_pad_format(&uds->entity, cfg,
UDS_PAD_SINK, code->which);
code->code = format->code;
}
return 0;
return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
ARRAY_SIZE(codes));
}
static int uds_enum_frame_size(struct v4l2_subdev *subdev,
@ -199,10 +129,15 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_uds *uds = to_uds(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
format = vsp1_entity_get_pad_format(&uds->entity, cfg,
UDS_PAD_SINK, fse->which);
config = vsp1_entity_get_pad_config(&uds->entity, cfg, fse->which);
if (!config)
return -EINVAL;
format = vsp1_entity_get_pad_format(&uds->entity, config,
UDS_PAD_SINK);
if (fse->index || fse->code != format->code)
return -EINVAL;
@ -222,20 +157,9 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev,
return 0;
}
static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_uds *uds = to_uds(subdev);
fmt->format = *vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad,
fmt->which);
return 0;
}
static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_pad_config *cfg,
unsigned int pad, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
static void uds_try_format(struct vsp1_uds *uds,
struct v4l2_subdev_pad_config *config,
unsigned int pad, struct v4l2_mbus_framefmt *fmt)
{
struct v4l2_mbus_framefmt *format;
unsigned int minimum;
@ -254,8 +178,8 @@ static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_pad_config *
case UDS_PAD_SOURCE:
/* The UDS scales but can't perform format conversion. */
format = vsp1_entity_get_pad_format(&uds->entity, cfg,
UDS_PAD_SINK, which);
format = vsp1_entity_get_pad_format(&uds->entity, config,
UDS_PAD_SINK);
fmt->code = format->code;
uds_output_limits(format->width, &minimum, &maximum);
@ -269,25 +193,30 @@ static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_pad_config *
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
static int uds_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_uds *uds = to_uds(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
uds_try_format(uds, cfg, fmt->pad, &fmt->format, fmt->which);
config = vsp1_entity_get_pad_config(&uds->entity, cfg, fmt->which);
if (!config)
return -EINVAL;
format = vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad,
fmt->which);
uds_try_format(uds, config, fmt->pad, &fmt->format);
format = vsp1_entity_get_pad_format(&uds->entity, config, fmt->pad);
*format = fmt->format;
if (fmt->pad == UDS_PAD_SINK) {
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&uds->entity, cfg,
UDS_PAD_SOURCE, fmt->which);
format = vsp1_entity_get_pad_format(&uds->entity, config,
UDS_PAD_SOURCE);
*format = fmt->format;
uds_try_format(uds, cfg, UDS_PAD_SOURCE, format, fmt->which);
uds_try_format(uds, config, UDS_PAD_SOURCE, format);
}
return 0;
@ -297,55 +226,97 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con
* 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 = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = uds_enum_mbus_code,
.enum_frame_size = uds_enum_frame_size,
.get_fmt = uds_get_format,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = uds_set_format,
};
static struct v4l2_subdev_ops uds_ops = {
.video = &uds_video_ops,
.pad = &uds_pad_ops,
};
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
static void uds_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
{
struct vsp1_uds *uds = to_uds(&entity->subdev);
const struct v4l2_mbus_framefmt *output;
const struct v4l2_mbus_framefmt *input;
unsigned int hscale;
unsigned int vscale;
bool multitap;
input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
UDS_PAD_SINK);
output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
UDS_PAD_SOURCE);
hscale = uds_compute_ratio(input->width, output->width);
vscale = uds_compute_ratio(input->height, output->height);
dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale);
/* Multi-tap scaling can't be enabled along with alpha scaling when
* scaling down with a factor lower than or equal to 1/2 in either
* direction.
*/
if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192))
multitap = false;
else
multitap = true;
vsp1_uds_write(uds, dl, VI6_UDS_CTRL,
(uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) |
(multitap ? VI6_UDS_CTRL_BC : 0));
vsp1_uds_write(uds, dl, VI6_UDS_PASS_BWIDTH,
(uds_passband_width(hscale)
<< VI6_UDS_PASS_BWIDTH_H_SHIFT) |
(uds_passband_width(vscale)
<< VI6_UDS_PASS_BWIDTH_V_SHIFT));
/* Set the scaling ratios and the output size. */
vsp1_uds_write(uds, dl, VI6_UDS_SCALE,
(hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
(vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE,
(output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
(output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
}
static const struct vsp1_entity_operations uds_entity_ops = {
.configure = uds_configure,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)
{
struct v4l2_subdev *subdev;
struct vsp1_uds *uds;
char name[6];
int ret;
uds = devm_kzalloc(vsp1->dev, sizeof(*uds), GFP_KERNEL);
if (uds == NULL)
return ERR_PTR(-ENOMEM);
uds->entity.ops = &uds_entity_ops;
uds->entity.type = VSP1_ENTITY_UDS;
uds->entity.index = index;
ret = vsp1_entity_init(vsp1, &uds->entity, 2);
sprintf(name, "uds.%u", index);
ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops);
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;
}

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