V4L/DVB (4197): Port new MPEG API to saa7134-empress with saa6752hs

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c
index a9587800..e8c65dd 100644
--- a/drivers/media/video/saa7134/saa6752hs.c
+++ b/drivers/media/video/saa7134/saa6752hs.c
@@ -39,6 +39,23 @@
 	SAA6752HS_VF_UNKNOWN,
 };
 
+struct saa6752hs_mpeg_params {
+	/* transport streams */
+	__u16				ts_pid_pmt;
+	__u16				ts_pid_audio;
+	__u16				ts_pid_video;
+	__u16				ts_pid_pcr;
+
+	/* audio */
+	enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate;
+
+	/* video */
+	enum v4l2_mpeg_video_aspect	vi_aspect;
+	enum v4l2_mpeg_video_bitrate_mode vi_bitrate_mode;
+	__u32 				vi_bitrate;
+	__u32 				vi_bitrate_peak;
+};
+
 static const struct v4l2_format v4l2_format_table[] =
 {
 	[SAA6752HS_VF_D1] =
@@ -55,7 +72,8 @@
 
 struct saa6752hs_state {
 	struct i2c_client             client;
-	struct v4l2_mpeg_compression  params;
+	struct v4l2_mpeg_compression  old_params;
+	struct saa6752hs_mpeg_params  params;
 	enum saa6752hs_videoformat    video_format;
 	v4l2_std_id                   standard;
 };
@@ -129,7 +147,22 @@
 	0x00, 0x00, 0x00, 0x00 /* CRC32 */
 };
 
-static struct v4l2_mpeg_compression param_defaults =
+static struct saa6752hs_mpeg_params param_defaults =
+{
+	.ts_pid_pmt      = 16,
+	.ts_pid_video    = 260,
+	.ts_pid_audio    = 256,
+	.ts_pid_pcr      = 259,
+
+	.vi_aspect       = V4L2_MPEG_VIDEO_ASPECT_4x3,
+	.vi_bitrate      = 4000,
+	.vi_bitrate_peak = 6000,
+	.vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+
+	.au_l2_bitrate   = V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+};
+
+static struct v4l2_mpeg_compression old_param_defaults =
 {
 	.st_type         = V4L2_MPEG_TS_2,
 	.st_bitrate      = {
@@ -318,50 +351,188 @@
 }
 
 
-static void saa6752hs_set_params(struct i2c_client* client,
+static void saa6752hs_old_set_params(struct i2c_client* client,
 				 struct v4l2_mpeg_compression* params)
 {
 	struct saa6752hs_state *h = i2c_get_clientdata(client);
 
 	/* check PIDs */
-	if (params->ts_pid_pmt <= MPEG_PID_MAX)
+	if (params->ts_pid_pmt <= MPEG_PID_MAX) {
+		h->old_params.ts_pid_pmt = params->ts_pid_pmt;
 		h->params.ts_pid_pmt = params->ts_pid_pmt;
-	if (params->ts_pid_pcr <= MPEG_PID_MAX)
+	}
+	if (params->ts_pid_pcr <= MPEG_PID_MAX) {
+		h->old_params.ts_pid_pcr = params->ts_pid_pcr;
 		h->params.ts_pid_pcr = params->ts_pid_pcr;
-	if (params->ts_pid_video <= MPEG_PID_MAX)
+	}
+	if (params->ts_pid_video <= MPEG_PID_MAX) {
+		h->old_params.ts_pid_video = params->ts_pid_video;
 		h->params.ts_pid_video = params->ts_pid_video;
-	if (params->ts_pid_audio <= MPEG_PID_MAX)
+	}
+	if (params->ts_pid_audio <= MPEG_PID_MAX) {
+		h->old_params.ts_pid_audio = params->ts_pid_audio;
 		h->params.ts_pid_audio = params->ts_pid_audio;
+	}
 
 	/* check bitrate parameters */
 	if ((params->vi_bitrate.mode == V4L2_BITRATE_CBR) ||
-	    (params->vi_bitrate.mode == V4L2_BITRATE_VBR))
-		h->params.vi_bitrate.mode = params->vi_bitrate.mode;
+	    (params->vi_bitrate.mode == V4L2_BITRATE_VBR)) {
+		h->old_params.vi_bitrate.mode = params->vi_bitrate.mode;
+		h->params.vi_bitrate_mode = (params->vi_bitrate.mode == V4L2_BITRATE_VBR) ?
+		       V4L2_MPEG_VIDEO_BITRATE_MODE_VBR : V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+	}
 	if (params->vi_bitrate.mode != V4L2_BITRATE_NONE)
-		h->params.st_bitrate.target = params->st_bitrate.target;
+		h->old_params.st_bitrate.target = params->st_bitrate.target;
 	if (params->vi_bitrate.mode != V4L2_BITRATE_NONE)
-		h->params.vi_bitrate.target = params->vi_bitrate.target;
+		h->old_params.vi_bitrate.target = params->vi_bitrate.target;
 	if (params->vi_bitrate.mode == V4L2_BITRATE_VBR)
-		h->params.vi_bitrate.max = params->vi_bitrate.max;
+		h->old_params.vi_bitrate.max = params->vi_bitrate.max;
 	if (params->au_bitrate.mode != V4L2_BITRATE_NONE)
-		h->params.au_bitrate.target = params->au_bitrate.target;
+		h->old_params.au_bitrate.target = params->au_bitrate.target;
 
 	/* aspect ratio */
 	if (params->vi_aspect_ratio == V4L2_MPEG_ASPECT_4_3 ||
-	    params->vi_aspect_ratio == V4L2_MPEG_ASPECT_16_9)
-		h->params.vi_aspect_ratio = params->vi_aspect_ratio;
+	    params->vi_aspect_ratio == V4L2_MPEG_ASPECT_16_9) {
+		h->old_params.vi_aspect_ratio = params->vi_aspect_ratio;
+		if (params->vi_aspect_ratio == V4L2_MPEG_ASPECT_4_3)
+			h->params.vi_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3;
+		else
+			h->params.vi_aspect = V4L2_MPEG_VIDEO_ASPECT_16x9;
+	}
 
 	/* range checks */
-	if (h->params.st_bitrate.target > MPEG_TOTAL_TARGET_BITRATE_MAX)
-		h->params.st_bitrate.target = MPEG_TOTAL_TARGET_BITRATE_MAX;
-	if (h->params.vi_bitrate.target > MPEG_VIDEO_TARGET_BITRATE_MAX)
-		h->params.vi_bitrate.target = MPEG_VIDEO_TARGET_BITRATE_MAX;
-	if (h->params.vi_bitrate.max > MPEG_VIDEO_MAX_BITRATE_MAX)
-		h->params.vi_bitrate.max = MPEG_VIDEO_MAX_BITRATE_MAX;
-	if (h->params.au_bitrate.target <= 256)
-		h->params.au_bitrate.target = 256;
+	if (h->old_params.st_bitrate.target > MPEG_TOTAL_TARGET_BITRATE_MAX)
+		h->old_params.st_bitrate.target = MPEG_TOTAL_TARGET_BITRATE_MAX;
+	if (h->old_params.vi_bitrate.target > MPEG_VIDEO_TARGET_BITRATE_MAX)
+		h->old_params.vi_bitrate.target = MPEG_VIDEO_TARGET_BITRATE_MAX;
+	if (h->old_params.vi_bitrate.max > MPEG_VIDEO_MAX_BITRATE_MAX)
+		h->old_params.vi_bitrate.max = MPEG_VIDEO_MAX_BITRATE_MAX;
+	h->params.vi_bitrate = params->vi_bitrate.target;
+	h->params.vi_bitrate_peak = params->vi_bitrate.max;
+	if (h->old_params.au_bitrate.target <= 256) {
+		h->old_params.au_bitrate.target = 256;
+		h->params.au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_256K;
+	}
+	else {
+		h->old_params.au_bitrate.target = 384;
+		h->params.au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_384K;
+	}
+}
+
+static int handle_ctrl(struct saa6752hs_mpeg_params *params,
+		struct v4l2_ext_control *ctrl, int cmd)
+{
+	int old = 0, new;
+	int set = cmd == VIDIOC_S_EXT_CTRLS;
+
+	new = ctrl->value;
+	switch (ctrl->id) {
+		case V4L2_CID_MPEG_STREAM_TYPE:
+			old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
+			if (set && new != old)
+				return -ERANGE;
+			new = old;
+			break;
+		case V4L2_CID_MPEG_STREAM_PID_PMT:
+			old = params->ts_pid_pmt;
+			if (set && new > MPEG_PID_MAX)
+				return -ERANGE;
+			if (new > MPEG_PID_MAX)
+				new = MPEG_PID_MAX;
+			params->ts_pid_pmt = new;
+			break;
+		case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+			old = params->ts_pid_audio;
+			if (set && new > MPEG_PID_MAX)
+				return -ERANGE;
+			if (new > MPEG_PID_MAX)
+				new = MPEG_PID_MAX;
+			params->ts_pid_audio = new;
+			break;
+		case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+			old = params->ts_pid_video;
+			if (set && new > MPEG_PID_MAX)
+				return -ERANGE;
+			if (new > MPEG_PID_MAX)
+				new = MPEG_PID_MAX;
+			params->ts_pid_video = new;
+			break;
+		case V4L2_CID_MPEG_STREAM_PID_PCR:
+			old = params->ts_pid_pcr;
+			if (set && new > MPEG_PID_MAX)
+				return -ERANGE;
+			if (new > MPEG_PID_MAX)
+				new = MPEG_PID_MAX;
+			params->ts_pid_pcr = new;
+			break;
+		case V4L2_CID_MPEG_AUDIO_ENCODING:
+			old = V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
+			if (set && new != old)
+				return -ERANGE;
+			new = old;
+			break;
+		case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+			old = params->au_l2_bitrate;
+			if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K &&
+				   new != V4L2_MPEG_AUDIO_L2_BITRATE_384K)
+				return -ERANGE;
+			if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K)
+				new = V4L2_MPEG_AUDIO_L2_BITRATE_256K;
+			else
+				new = V4L2_MPEG_AUDIO_L2_BITRATE_384K;
+			params->au_l2_bitrate = new;
+			break;
+		case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+			old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
+			if (set && new != old)
+				return -ERANGE;
+			new = old;
+			break;
+		case V4L2_CID_MPEG_VIDEO_ENCODING:
+			old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+			if (set && new != old)
+				return -ERANGE;
+			new = old;
+			break;
+		case V4L2_CID_MPEG_VIDEO_ASPECT:
+			old = params->vi_aspect;
+			if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 &&
+				   new != V4L2_MPEG_VIDEO_ASPECT_4x3)
+				return -ERANGE;
+			if (new != V4L2_MPEG_VIDEO_ASPECT_16x9)
+				new = V4L2_MPEG_VIDEO_ASPECT_4x3;
+			params->vi_aspect = new;
+			break;
+		case V4L2_CID_MPEG_VIDEO_BITRATE:
+			old = params->vi_bitrate * 1000;
+			new = 1000 * (new / 1000);
+			if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+				return -ERANGE;
+			if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+				new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
+			params->vi_bitrate = new / 1000;
+			break;
+		case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+			old = params->vi_bitrate_peak * 1000;
+			new = 1000 * (new / 1000);
+			if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+				return -ERANGE;
+			if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+				new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
+			params->vi_bitrate_peak = new / 1000;
+			break;
+		case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+			old = params->vi_bitrate_mode;
+			params->vi_bitrate_mode = new;
+			break;
+		default:
+			return -EINVAL;
+	}
+	if (cmd == VIDIOC_G_EXT_CTRLS)
+		ctrl->value = old;
 	else
-		h->params.au_bitrate.target = 384;
+		ctrl->value = new;
+	return 0;
 }
 
 static int saa6752hs_init(struct i2c_client* client)
@@ -489,11 +660,11 @@
 	buf[3] = 0x82;
 	buf[4] = 0xB0;
 	buf[5] = buf2[0];
-	switch(h->params.vi_aspect_ratio) {
-	case V4L2_MPEG_ASPECT_16_9:
+	switch(h->params.vi_aspect) {
+	case V4L2_MPEG_VIDEO_ASPECT_16x9:
 		buf[6] = buf2[1] | 0x40;
 		break;
-	case V4L2_MPEG_ASPECT_4_3:
+	case V4L2_MPEG_VIDEO_ASPECT_4x3:
 	default:
 		buf[6] = buf2[1] & 0xBF;
 		break;
@@ -515,6 +686,7 @@
 		return -ENOMEM;
 	h->client = client_template;
 	h->params = param_defaults;
+	h->old_params = old_param_defaults;
 	h->client.adapter = adap;
 	h->client.addr = addr;
 
@@ -550,20 +722,45 @@
 saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg)
 {
 	struct saa6752hs_state *h = i2c_get_clientdata(client);
-	struct v4l2_mpeg_compression *params = arg;
+	struct v4l2_ext_controls *ctrls = arg;
+	struct v4l2_mpeg_compression *old_params = arg;
+	struct saa6752hs_mpeg_params params;
 	int err = 0;
+	int i;
 
 	switch (cmd) {
 	case VIDIOC_S_MPEGCOMP:
-		if (NULL == params) {
+		if (NULL == old_params) {
 			/* apply settings and start encoder */
 			saa6752hs_init(client);
 			break;
 		}
-		saa6752hs_set_params(client, params);
+		saa6752hs_old_set_params(client, old_params);
 		/* fall through */
 	case VIDIOC_G_MPEGCOMP:
-		*params = h->params;
+		*old_params = h->old_params;
+		break;
+	case VIDIOC_S_EXT_CTRLS:
+		if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+			return -EINVAL;
+		if (ctrls->count == 0) {
+			/* apply settings and start encoder */
+			saa6752hs_init(client);
+			break;
+		}
+		/* fall through */
+	case VIDIOC_TRY_EXT_CTRLS:
+	case VIDIOC_G_EXT_CTRLS:
+		if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+			return -EINVAL;
+		params = h->params;
+		for (i = 0; i < ctrls->count; i++) {
+			if ((err = handle_ctrl(&params, ctrls->controls + i, cmd))) {
+				ctrls->error_idx = i;
+				return err;
+			}
+		}
+		h->params = params;
 		break;
 	case VIDIOC_G_FMT:
 	{
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
index 1d972ed..bc743b9 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -64,8 +64,10 @@
 
 static int ts_init_encoder(struct saa7134_dev* dev)
 {
+	struct v4l2_ext_controls ctrls = { V4L2_CTRL_CLASS_MPEG, 0 };
+
 	ts_reset_encoder(dev);
-	saa7134_i2c_call_clients(dev, VIDIOC_S_MPEGCOMP, NULL);
+	saa7134_i2c_call_clients(dev, VIDIOC_S_EXT_CTRLS, &ctrls);
 	dev->empress_started = 1;
 	return 0;
 }
@@ -162,6 +164,7 @@
 		       unsigned int cmd, void *arg)
 {
 	struct saa7134_dev *dev = file->private_data;
+	struct v4l2_ext_controls *ctrls = arg;
 
 	if (debug > 1)
 		v4l_print_ioctl(dev->name,cmd);
@@ -284,6 +287,21 @@
 	case VIDIOC_G_MPEGCOMP:
 		saa7134_i2c_call_clients(dev, VIDIOC_G_MPEGCOMP, arg);
 		return 0;
+	case VIDIOC_S_EXT_CTRLS:
+		/* count == 0 is abused in saa6752hs.c, so that special
+		   case is handled here explicitly. */
+		if (ctrls->count == 0)
+			return 0;
+		if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+			return -EINVAL;
+		saa7134_i2c_call_clients(dev, VIDIOC_S_EXT_CTRLS, arg);
+		ts_init_encoder(dev);
+		return 0;
+	case VIDIOC_G_EXT_CTRLS:
+		if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+			return -EINVAL;
+		saa7134_i2c_call_clients(dev, VIDIOC_G_EXT_CTRLS, arg);
+		return 0;
 
 	default:
 		return -ENOIOCTLCMD;