162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci ioctl control functions 462306a36Sopenharmony_ci Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> 562306a36Sopenharmony_ci Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "ivtv-driver.h" 1062306a36Sopenharmony_ci#include "ivtv-ioctl.h" 1162306a36Sopenharmony_ci#include "ivtv-controls.h" 1262306a36Sopenharmony_ci#include "ivtv-mailbox.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci /* First try to allocate sliced VBI buffers if needed. */ 1962306a36Sopenharmony_ci if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) { 2062306a36Sopenharmony_ci int i; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci for (i = 0; i < IVTV_VBI_FRAMES; i++) { 2362306a36Sopenharmony_ci /* Yuck, hardcoded. Needs to be a define */ 2462306a36Sopenharmony_ci itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL); 2562306a36Sopenharmony_ci if (itv->vbi.sliced_mpeg_data[i] == NULL) { 2662306a36Sopenharmony_ci while (--i >= 0) { 2762306a36Sopenharmony_ci kfree(itv->vbi.sliced_mpeg_data[i]); 2862306a36Sopenharmony_ci itv->vbi.sliced_mpeg_data[i] = NULL; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci return -ENOMEM; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci itv->vbi.insert_mpeg = fmt; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (itv->vbi.insert_mpeg == 0) { 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci /* Need sliced data for mpeg insertion */ 4162306a36Sopenharmony_ci if (ivtv_get_service_set(itv->vbi.sliced_in) == 0) { 4262306a36Sopenharmony_ci if (itv->is_60hz) 4362306a36Sopenharmony_ci itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; 4462306a36Sopenharmony_ci else 4562306a36Sopenharmony_ci itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; 4662306a36Sopenharmony_ci ivtv_expand_service_set(itv->vbi.sliced_in, itv->is_50hz); 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); 5462306a36Sopenharmony_ci int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; 5562306a36Sopenharmony_ci struct v4l2_subdev_format format = { 5662306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 5762306a36Sopenharmony_ci }; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* fix videodecoder resolution */ 6062306a36Sopenharmony_ci format.format.width = cxhdl->width / (is_mpeg1 ? 2 : 1); 6162306a36Sopenharmony_ci format.format.height = cxhdl->height; 6262306a36Sopenharmony_ci format.format.code = MEDIA_BUS_FMT_FIXED; 6362306a36Sopenharmony_ci v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, &format); 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci static const u32 freqs[3] = { 44100, 48000, 32000 }; 7062306a36Sopenharmony_ci struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* The audio clock of the digitizer must match the codec sample 7362306a36Sopenharmony_ci rate otherwise you get some very strange effects. */ 7462306a36Sopenharmony_ci if (idx < ARRAY_SIZE(freqs)) 7562306a36Sopenharmony_ci ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci itv->dualwatch_stereo_mode = val; 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciconst struct cx2341x_handler_ops ivtv_cxhdl_ops = { 8862306a36Sopenharmony_ci .s_audio_mode = ivtv_s_audio_mode, 8962306a36Sopenharmony_ci .s_audio_sampling_freq = ivtv_s_audio_sampling_freq, 9062306a36Sopenharmony_ci .s_video_encoding = ivtv_s_video_encoding, 9162306a36Sopenharmony_ci .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ciint ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { 9962306a36Sopenharmony_ci *pts = (s64)((u64)itv->last_dec_timing[2] << 32) | 10062306a36Sopenharmony_ci (u64)itv->last_dec_timing[1]; 10162306a36Sopenharmony_ci *frame = itv->last_dec_timing[0]; 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci *pts = 0; 10562306a36Sopenharmony_ci *frame = 0; 10662306a36Sopenharmony_ci if (atomic_read(&itv->decoding)) { 10762306a36Sopenharmony_ci if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { 10862306a36Sopenharmony_ci IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); 10962306a36Sopenharmony_ci return -EIO; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); 11262306a36Sopenharmony_ci set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); 11362306a36Sopenharmony_ci *pts = (s64)((u64) data[2] << 32) | (u64) data[1]; 11462306a36Sopenharmony_ci *frame = data[0]; 11562306a36Sopenharmony_ci /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/ 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci switch (ctrl->id) { 12562306a36Sopenharmony_ci /* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME 12662306a36Sopenharmony_ci control cluster */ 12762306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_DEC_PTS: 12862306a36Sopenharmony_ci return ivtv_g_pts_frame(itv, itv->ctrl_pts->p_new.p_s64, 12962306a36Sopenharmony_ci itv->ctrl_frame->p_new.p_s64); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int ivtv_s_ctrl(struct v4l2_ctrl *ctrl) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci switch (ctrl->id) { 13962306a36Sopenharmony_ci /* V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK and MULTILINGUAL_PLAYBACK 14062306a36Sopenharmony_ci control cluster */ 14162306a36Sopenharmony_ci case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: 14262306a36Sopenharmony_ci itv->audio_stereo_mode = itv->ctrl_audio_playback->val - 1; 14362306a36Sopenharmony_ci itv->audio_bilingual_mode = itv->ctrl_audio_multilingual_playback->val - 1; 14462306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciconst struct v4l2_ctrl_ops ivtv_hdl_out_ops = { 15162306a36Sopenharmony_ci .s_ctrl = ivtv_s_ctrl, 15262306a36Sopenharmony_ci .g_volatile_ctrl = ivtv_g_volatile_ctrl, 15362306a36Sopenharmony_ci}; 154