1/*
2 *  cx18 ioctl control functions
3 *
4 *  Derived from ivtv-controls.c
5 *
6 *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
7 *
8 *  This program is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation; either version 2 of the License, or
11 *  (at your option) any later version.
12 *
13 *  This program is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this program; if not, write to the Free Software
20 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 *  02111-1307  USA
22 */
23#include <linux/kernel.h>
24#include <linux/slab.h>
25
26#include "cx18-driver.h"
27#include "cx18-cards.h"
28#include "cx18-ioctl.h"
29#include "cx18-audio.h"
30#include "cx18-mailbox.h"
31#include "cx18-controls.h"
32
33static int cx18_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
34{
35	struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
36	int type = cxhdl->stream_type->val;
37
38	if (atomic_read(&cx->ana_capturing) > 0)
39		return -EBUSY;
40
41	if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV ||
42	    !(type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS ||
43	      type == V4L2_MPEG_STREAM_TYPE_MPEG2_DVD ||
44	      type == V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD)) {
45		/* Only IVTV fmt VBI insertion & only MPEG-2 PS type streams */
46		cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE;
47		CX18_DEBUG_INFO("disabled insertion of sliced VBI data into "
48				"the MPEG stream\n");
49		return 0;
50	}
51
52	/* Allocate sliced VBI buffers if needed. */
53	if (cx->vbi.sliced_mpeg_data[0] == NULL) {
54		int i;
55
56		for (i = 0; i < CX18_VBI_FRAMES; i++) {
57			cx->vbi.sliced_mpeg_data[i] =
58			       kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, GFP_KERNEL);
59			if (cx->vbi.sliced_mpeg_data[i] == NULL) {
60				while (--i >= 0) {
61					kfree(cx->vbi.sliced_mpeg_data[i]);
62					cx->vbi.sliced_mpeg_data[i] = NULL;
63				}
64				cx->vbi.insert_mpeg =
65						  V4L2_MPEG_STREAM_VBI_FMT_NONE;
66				CX18_WARN("Unable to allocate buffers for "
67					  "sliced VBI data insertion\n");
68				return -ENOMEM;
69			}
70		}
71	}
72
73	cx->vbi.insert_mpeg = fmt;
74	CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS,"
75			"when sliced VBI is enabled\n");
76
77	/*
78	 * If our current settings have no lines set for capture, store a valid,
79	 * default set of service lines to capture, in our current settings.
80	 */
81	if (cx18_get_service_set(cx->vbi.sliced_in) == 0) {
82		if (cx->is_60hz)
83			cx->vbi.sliced_in->service_set =
84							V4L2_SLICED_CAPTION_525;
85		else
86			cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
87		cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
88	}
89	return 0;
90}
91
92static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
93{
94	struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
95	int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
96	struct v4l2_subdev_format format = {
97		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
98	};
99	struct v4l2_mbus_framefmt *fmt = &format.format;
100
101	/* fix videodecoder resolution */
102	fmt->width = cxhdl->width / (is_mpeg1 ? 2 : 1);
103	fmt->height = cxhdl->height;
104	fmt->code = MEDIA_BUS_FMT_FIXED;
105	v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, &format);
106	return 0;
107}
108
109static int cx18_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
110{
111	static const u32 freqs[3] = { 44100, 48000, 32000 };
112	struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
113
114	/* The audio clock of the digitizer must match the codec sample
115	   rate otherwise you get some very strange effects. */
116	if (idx < ARRAY_SIZE(freqs))
117		cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
118	return 0;
119}
120
121static int cx18_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
122{
123	struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
124
125	cx->dualwatch_stereo_mode = val;
126	return 0;
127}
128
129struct cx2341x_handler_ops cx18_cxhdl_ops = {
130	.s_audio_mode = cx18_s_audio_mode,
131	.s_audio_sampling_freq = cx18_s_audio_sampling_freq,
132	.s_video_encoding = cx18_s_video_encoding,
133	.s_stream_vbi_fmt = cx18_s_stream_vbi_fmt,
134};
135