18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  cx18 audio-related functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Derived from ivtv-audio.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "cx18-driver.h"
118c2ecf20Sopenharmony_ci#include "cx18-io.h"
128c2ecf20Sopenharmony_ci#include "cx18-cards.h"
138c2ecf20Sopenharmony_ci#include "cx18-audio.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define CX18_AUDIO_ENABLE    0xc72014
168c2ecf20Sopenharmony_ci#define CX18_AI1_MUX_MASK    0x30
178c2ecf20Sopenharmony_ci#define CX18_AI1_MUX_I2S1    0x00
188c2ecf20Sopenharmony_ci#define CX18_AI1_MUX_I2S2    0x10
198c2ecf20Sopenharmony_ci#define CX18_AI1_MUX_843_I2S 0x20
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* Selects the audio input and output according to the current
228c2ecf20Sopenharmony_ci   settings. */
238c2ecf20Sopenharmony_ciint cx18_audio_set_io(struct cx18 *cx)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	const struct cx18_card_audio_input *in;
268c2ecf20Sopenharmony_ci	u32 u, v;
278c2ecf20Sopenharmony_ci	int err;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	/* Determine which input to use */
308c2ecf20Sopenharmony_ci	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
318c2ecf20Sopenharmony_ci		in = &cx->card->radio_input;
328c2ecf20Sopenharmony_ci	else
338c2ecf20Sopenharmony_ci		in = &cx->card->audio_inputs[cx->audio_input];
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/* handle muxer chips */
368c2ecf20Sopenharmony_ci	v4l2_subdev_call(cx->sd_extmux, audio, s_routing,
378c2ecf20Sopenharmony_ci			 (u32) in->muxer_input, 0, 0);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
408c2ecf20Sopenharmony_ci			       audio, s_routing, in->audio_input, 0, 0);
418c2ecf20Sopenharmony_ci	if (err)
428c2ecf20Sopenharmony_ci		return err;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* FIXME - this internal mux should be abstracted to a subdev */
458c2ecf20Sopenharmony_ci	u = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
468c2ecf20Sopenharmony_ci	v = u & ~CX18_AI1_MUX_MASK;
478c2ecf20Sopenharmony_ci	switch (in->audio_input) {
488c2ecf20Sopenharmony_ci	case CX18_AV_AUDIO_SERIAL1:
498c2ecf20Sopenharmony_ci		v |= CX18_AI1_MUX_I2S1;
508c2ecf20Sopenharmony_ci		break;
518c2ecf20Sopenharmony_ci	case CX18_AV_AUDIO_SERIAL2:
528c2ecf20Sopenharmony_ci		v |= CX18_AI1_MUX_I2S2;
538c2ecf20Sopenharmony_ci		break;
548c2ecf20Sopenharmony_ci	default:
558c2ecf20Sopenharmony_ci		v |= CX18_AI1_MUX_843_I2S;
568c2ecf20Sopenharmony_ci		break;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci	if (v == u) {
598c2ecf20Sopenharmony_ci		/* force a toggle of some AI1 MUX control bits */
608c2ecf20Sopenharmony_ci		u &= ~CX18_AI1_MUX_MASK;
618c2ecf20Sopenharmony_ci		switch (in->audio_input) {
628c2ecf20Sopenharmony_ci		case CX18_AV_AUDIO_SERIAL1:
638c2ecf20Sopenharmony_ci			u |= CX18_AI1_MUX_843_I2S;
648c2ecf20Sopenharmony_ci			break;
658c2ecf20Sopenharmony_ci		case CX18_AV_AUDIO_SERIAL2:
668c2ecf20Sopenharmony_ci			u |= CX18_AI1_MUX_843_I2S;
678c2ecf20Sopenharmony_ci			break;
688c2ecf20Sopenharmony_ci		default:
698c2ecf20Sopenharmony_ci			u |= CX18_AI1_MUX_I2S1;
708c2ecf20Sopenharmony_ci			break;
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci		cx18_write_reg_expect(cx, u | 0xb00, CX18_AUDIO_ENABLE,
738c2ecf20Sopenharmony_ci				      u, CX18_AI1_MUX_MASK);
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
768c2ecf20Sopenharmony_ci			      v, CX18_AI1_MUX_MASK);
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci}
79