xref: /netbsd-src/sys/arch/arm/sunxi/sun8i_codec.c (revision 080a694d36744a16eb7fa7d56ca9baeea190f161)
1*080a694dSjmcneill /* $NetBSD: sun8i_codec.c,v 1.10 2022/10/29 19:07:39 jmcneill Exp $ */
244ca330bSjmcneill 
344ca330bSjmcneill /*-
444ca330bSjmcneill  * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
544ca330bSjmcneill  * All rights reserved.
644ca330bSjmcneill  *
744ca330bSjmcneill  * Redistribution and use in source and binary forms, with or without
844ca330bSjmcneill  * modification, are permitted provided that the following conditions
944ca330bSjmcneill  * are met:
1044ca330bSjmcneill  * 1. Redistributions of source code must retain the above copyright
1144ca330bSjmcneill  *    notice, this list of conditions and the following disclaimer.
1244ca330bSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1344ca330bSjmcneill  *    notice, this list of conditions and the following disclaimer in the
1444ca330bSjmcneill  *    documentation and/or other materials provided with the distribution.
1544ca330bSjmcneill  *
1644ca330bSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1744ca330bSjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1844ca330bSjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1944ca330bSjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2044ca330bSjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2144ca330bSjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2244ca330bSjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2344ca330bSjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2444ca330bSjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2544ca330bSjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2644ca330bSjmcneill  * SUCH DAMAGE.
2744ca330bSjmcneill  */
2844ca330bSjmcneill 
2944ca330bSjmcneill #include <sys/cdefs.h>
30*080a694dSjmcneill __KERNEL_RCSID(0, "$NetBSD: sun8i_codec.c,v 1.10 2022/10/29 19:07:39 jmcneill Exp $");
3144ca330bSjmcneill 
3244ca330bSjmcneill #include <sys/param.h>
3344ca330bSjmcneill #include <sys/bus.h>
3444ca330bSjmcneill #include <sys/cpu.h>
3544ca330bSjmcneill #include <sys/device.h>
3644ca330bSjmcneill #include <sys/kmem.h>
3744ca330bSjmcneill #include <sys/bitops.h>
3844ca330bSjmcneill #include <sys/gpio.h>
3976bf467cSjmcneill #include <sys/workqueue.h>
4044ca330bSjmcneill 
41e622eac4Sisaki #include <dev/audio/audio_dai.h>
4244ca330bSjmcneill 
4344ca330bSjmcneill #include <dev/fdt/fdtvar.h>
4444ca330bSjmcneill 
4544ca330bSjmcneill #define	SYSCLK_CTL		0x00c
4644ca330bSjmcneill #define	 AIF1CLK_ENA		__BIT(11)
4744ca330bSjmcneill #define	 AIF1CLK_SRC		__BITS(9,8)
4844ca330bSjmcneill #define	  AIF1CLK_SRC_PLL	2
4944ca330bSjmcneill #define	 SYSCLK_ENA		__BIT(3)
5044ca330bSjmcneill #define	 SYSCLK_SRC		__BIT(0)
5144ca330bSjmcneill 
5244ca330bSjmcneill #define	MOD_CLK_ENA		0x010
5344ca330bSjmcneill #define	MOD_RST_CTL		0x014
5444ca330bSjmcneill #define	 MOD_AIF1		__BIT(15)
5544ca330bSjmcneill #define	 MOD_ADC		__BIT(3)
5644ca330bSjmcneill #define	 MOD_DAC		__BIT(2)
5744ca330bSjmcneill 
5844ca330bSjmcneill #define	SYS_SR_CTRL		0x018
5944ca330bSjmcneill #define	 AIF1_FS		__BITS(15,12)
6044ca330bSjmcneill #define	  AIF_FS_48KHZ		8
6144ca330bSjmcneill 
6244ca330bSjmcneill #define	AIF1CLK_CTRL		0x040
6344ca330bSjmcneill #define	 AIF1_MSTR_MOD		__BIT(15)
6444ca330bSjmcneill #define	 AIF1_BCLK_INV		__BIT(14)
6544ca330bSjmcneill #define	 AIF1_LRCK_INV		__BIT(13)
6644ca330bSjmcneill #define	 AIF1_BCLK_DIV		__BITS(12,9)
6744ca330bSjmcneill #define	  AIF1_BCLK_DIV_16	6
6844ca330bSjmcneill #define	 AIF1_LRCK_DIV		__BITS(8,6)
6944ca330bSjmcneill #define	  AIF1_LRCK_DIV_16	0
7044ca330bSjmcneill #define	  AIF1_LRCK_DIV_64	2
7144ca330bSjmcneill #define	 AIF1_WORD_SIZ		__BITS(5,4)
7244ca330bSjmcneill #define	  AIF1_WORD_SIZ_16	1
7344ca330bSjmcneill #define	 AIF1_DATA_FMT		__BITS(3,2)
7444ca330bSjmcneill #define	  AIF1_DATA_FMT_I2S	0
7544ca330bSjmcneill #define	  AIF1_DATA_FMT_LJ	1
7644ca330bSjmcneill #define	  AIF1_DATA_FMT_RJ	2
7744ca330bSjmcneill #define	  AIF1_DATA_FMT_DSP	3
7844ca330bSjmcneill 
7944ca330bSjmcneill #define	AIF1_DACDAT_CTRL	0x048
8044ca330bSjmcneill #define	 AIF1_DAC0L_ENA		__BIT(15)
8144ca330bSjmcneill #define	 AIF1_DAC0R_ENA		__BIT(14)
8244ca330bSjmcneill 
8344ca330bSjmcneill #define	ADC_DIG_CTRL		0x100
8444ca330bSjmcneill #define	 ADC_DIG_CTRL_ENAD	__BIT(15)
8544ca330bSjmcneill 
8676bf467cSjmcneill #define	HMIC_CTRL1		0x110
8776bf467cSjmcneill #define	 HMIC_CTRL1_N		__BITS(11,8)
8876bf467cSjmcneill #define	 HMIC_CTRL1_JACK_IN_IRQ_EN __BIT(4)
8976bf467cSjmcneill #define	 HMIC_CTRL1_JACK_OUT_IRQ_EN __BIT(3)
9076bf467cSjmcneill #define	 HMIC_CTRL1_MIC_DET_IRQ_EN __BIT(0)
9176bf467cSjmcneill 
9276bf467cSjmcneill #define	HMIC_CTRL2		0x114
9376bf467cSjmcneill #define	 HMIC_CTRL2_MDATA_THRES	__BITS(12,8)
9476bf467cSjmcneill 
9576bf467cSjmcneill #define	HMIC_STS		0x118
9676bf467cSjmcneill #define	 HMIC_STS_MIC_PRESENT	__BIT(6)
9776bf467cSjmcneill #define	 HMIC_STS_JACK_DET_OIRQ	__BIT(4)
9876bf467cSjmcneill #define	 HMIC_STS_JACK_DET_IIRQ	__BIT(3)
9976bf467cSjmcneill #define	 HMIC_STS_MIC_DET_ST	__BIT(0)
10076bf467cSjmcneill 
10144ca330bSjmcneill #define	DAC_DIG_CTRL		0x120
10244ca330bSjmcneill #define	 DAC_DIG_CTRL_ENDA	__BIT(15)
10344ca330bSjmcneill 
10444ca330bSjmcneill #define	DAC_MXR_SRC		0x130
10544ca330bSjmcneill #define	 DACL_MXR_SRC		__BITS(15,12)
10644ca330bSjmcneill #define	  DACL_MXR_SRC_AIF1_DAC0L 0x8
10744ca330bSjmcneill #define	 DACR_MXR_SRC		__BITS(11,8)
10844ca330bSjmcneill #define	  DACR_MXR_SRC_AIF1_DAC0R 0x8
10944ca330bSjmcneill 
11044ca330bSjmcneill struct sun8i_codec_softc {
11144ca330bSjmcneill 	device_t		sc_dev;
11244ca330bSjmcneill 	bus_space_tag_t		sc_bst;
11344ca330bSjmcneill 	bus_space_handle_t	sc_bsh;
11444ca330bSjmcneill 	int			sc_phandle;
11544ca330bSjmcneill 
11676bf467cSjmcneill 	struct workqueue	*sc_workq;
11776bf467cSjmcneill 	struct work		sc_work;
11876bf467cSjmcneill 
11944ca330bSjmcneill 	struct audio_dai_device	sc_dai;
12076bf467cSjmcneill 	audio_dai_tag_t		sc_codec_analog;
12176bf467cSjmcneill 	int			sc_jackdet_pol;
12244ca330bSjmcneill 
12344ca330bSjmcneill 	struct fdtbus_gpio_pin	*sc_pin_pa;
12444ca330bSjmcneill 
12544ca330bSjmcneill 	struct clk		*sc_clk_gate;
12644ca330bSjmcneill 	struct clk		*sc_clk_mod;
12744ca330bSjmcneill };
12844ca330bSjmcneill 
12944ca330bSjmcneill #define	RD4(sc, reg)			\
13044ca330bSjmcneill 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
13144ca330bSjmcneill #define	WR4(sc, reg, val)		\
13244ca330bSjmcneill 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
13344ca330bSjmcneill 
13444ca330bSjmcneill static int
sun8i_codec_set_port(void * priv,mixer_ctrl_t * mc)135c0b821c0Sjmcneill sun8i_codec_set_port(void *priv, mixer_ctrl_t *mc)
136c0b821c0Sjmcneill {
137c0b821c0Sjmcneill 	struct sun8i_codec_softc * const sc = priv;
138c0b821c0Sjmcneill 
139c0b821c0Sjmcneill 	if (!sc->sc_codec_analog)
140c0b821c0Sjmcneill 		return ENXIO;
141c0b821c0Sjmcneill 
142c0b821c0Sjmcneill 	return audio_dai_set_port(sc->sc_codec_analog, mc);
143c0b821c0Sjmcneill }
144c0b821c0Sjmcneill 
145c0b821c0Sjmcneill static int
sun8i_codec_get_port(void * priv,mixer_ctrl_t * mc)146c0b821c0Sjmcneill sun8i_codec_get_port(void *priv, mixer_ctrl_t *mc)
147c0b821c0Sjmcneill {
148c0b821c0Sjmcneill 	struct sun8i_codec_softc * const sc = priv;
149c0b821c0Sjmcneill 
150c0b821c0Sjmcneill 	if (!sc->sc_codec_analog)
151c0b821c0Sjmcneill 		return ENXIO;
152c0b821c0Sjmcneill 
153c0b821c0Sjmcneill 	return audio_dai_get_port(sc->sc_codec_analog, mc);
154c0b821c0Sjmcneill }
155c0b821c0Sjmcneill 
156c0b821c0Sjmcneill static int
sun8i_codec_query_devinfo(void * priv,mixer_devinfo_t * di)157c0b821c0Sjmcneill sun8i_codec_query_devinfo(void *priv, mixer_devinfo_t *di)
158c0b821c0Sjmcneill {
159c0b821c0Sjmcneill 	struct sun8i_codec_softc * const sc = priv;
160c0b821c0Sjmcneill 
161c0b821c0Sjmcneill 	if (!sc->sc_codec_analog)
162c0b821c0Sjmcneill 		return ENXIO;
163c0b821c0Sjmcneill 
164c0b821c0Sjmcneill 	return audio_dai_query_devinfo(sc->sc_codec_analog, di);
165c0b821c0Sjmcneill }
166c0b821c0Sjmcneill 
16744ca330bSjmcneill static const struct audio_hw_if sun8i_codec_hw_if = {
168c0b821c0Sjmcneill 	.set_port = sun8i_codec_set_port,
169c0b821c0Sjmcneill 	.get_port = sun8i_codec_get_port,
170c0b821c0Sjmcneill 	.query_devinfo = sun8i_codec_query_devinfo,
17144ca330bSjmcneill };
17244ca330bSjmcneill 
17344ca330bSjmcneill static audio_dai_tag_t
sun8i_codec_dai_get_tag(device_t dev,const void * data,size_t len)17444ca330bSjmcneill sun8i_codec_dai_get_tag(device_t dev, const void *data, size_t len)
17544ca330bSjmcneill {
17644ca330bSjmcneill 	struct sun8i_codec_softc * const sc = device_private(dev);
177*080a694dSjmcneill 	const u_int sound_dai_cells = len / 4;
17844ca330bSjmcneill 
179*080a694dSjmcneill 	KASSERT(sound_dai_cells > 0);
18044ca330bSjmcneill 
181*080a694dSjmcneill 	/*
182*080a694dSjmcneill 	 * This driver only supports AIF1 with CPU DAI at the moment.
183*080a694dSjmcneill 	 * When #sound-dai-cells is 0, return this tag. When #sound-dai-cells
184*080a694dSjmcneill 	 * is 1, return this tag only when the second cell contains the
185*080a694dSjmcneill 	 * value 0.
186*080a694dSjmcneill 	 *
187*080a694dSjmcneill 	 * Update this when support for multiple interfaces is added to
188*080a694dSjmcneill 	 * this driver.
189*080a694dSjmcneill 	 */
190*080a694dSjmcneill 	if (sound_dai_cells == 1) {
19144ca330bSjmcneill 		return &sc->sc_dai;
19244ca330bSjmcneill 	}
19344ca330bSjmcneill 
194*080a694dSjmcneill 	if (sound_dai_cells == 2) {
195*080a694dSjmcneill 		const u_int iface = be32dec((const u_int *)data + 1);
196*080a694dSjmcneill 		if (iface == 0) {
197*080a694dSjmcneill 			return &sc->sc_dai;
198*080a694dSjmcneill 		}
199*080a694dSjmcneill 	}
200*080a694dSjmcneill 
201*080a694dSjmcneill 	return NULL;
202*080a694dSjmcneill }
203*080a694dSjmcneill 
20444ca330bSjmcneill static struct fdtbus_dai_controller_func sun8i_codec_dai_funcs = {
20544ca330bSjmcneill 	.get_tag = sun8i_codec_dai_get_tag
20644ca330bSjmcneill };
20744ca330bSjmcneill 
20844ca330bSjmcneill static int
sun8i_codec_dai_set_format(audio_dai_tag_t dai,u_int format)20944ca330bSjmcneill sun8i_codec_dai_set_format(audio_dai_tag_t dai, u_int format)
21044ca330bSjmcneill {
21144ca330bSjmcneill 	struct sun8i_codec_softc * const sc = audio_dai_private(dai);
21244ca330bSjmcneill 	uint32_t val;
21344ca330bSjmcneill 
21444ca330bSjmcneill         const u_int fmt = __SHIFTOUT(format, AUDIO_DAI_FORMAT_MASK);
21544ca330bSjmcneill         const u_int pol = __SHIFTOUT(format, AUDIO_DAI_POLARITY_MASK);
21644ca330bSjmcneill         const u_int clk = __SHIFTOUT(format, AUDIO_DAI_CLOCK_MASK);
21744ca330bSjmcneill 
21844ca330bSjmcneill 	val = RD4(sc, AIF1CLK_CTRL);
21944ca330bSjmcneill 
22044ca330bSjmcneill 	val &= ~AIF1_DATA_FMT;
22144ca330bSjmcneill 	switch (fmt) {
22244ca330bSjmcneill 	case AUDIO_DAI_FORMAT_I2S:
22344ca330bSjmcneill 		val |= __SHIFTIN(AIF1_DATA_FMT_I2S, AIF1_DATA_FMT);
22444ca330bSjmcneill 		break;
22544ca330bSjmcneill 	case AUDIO_DAI_FORMAT_RJ:
22644ca330bSjmcneill 		val |= __SHIFTIN(AIF1_DATA_FMT_RJ, AIF1_DATA_FMT);
22744ca330bSjmcneill 		break;
22844ca330bSjmcneill 	case AUDIO_DAI_FORMAT_LJ:
22944ca330bSjmcneill 		val |= __SHIFTIN(AIF1_DATA_FMT_LJ, AIF1_DATA_FMT);
23044ca330bSjmcneill 		break;
23144ca330bSjmcneill 	case AUDIO_DAI_FORMAT_DSPA:
23244ca330bSjmcneill 	case AUDIO_DAI_FORMAT_DSPB:
23344ca330bSjmcneill 		val |= __SHIFTIN(AIF1_DATA_FMT_DSP, AIF1_DATA_FMT);
23444ca330bSjmcneill 		break;
23544ca330bSjmcneill 	default:
23644ca330bSjmcneill 		return EINVAL;
23744ca330bSjmcneill 	}
23844ca330bSjmcneill 
23944ca330bSjmcneill 	val &= ~(AIF1_BCLK_INV|AIF1_LRCK_INV);
24044ca330bSjmcneill 	/* Codec LRCK polarity is inverted (datasheet is wrong) */
24144ca330bSjmcneill 	if (!AUDIO_DAI_POLARITY_F(pol))
24244ca330bSjmcneill 		val |= AIF1_LRCK_INV;
24344ca330bSjmcneill 	if (AUDIO_DAI_POLARITY_B(pol))
24444ca330bSjmcneill 		val |= AIF1_BCLK_INV;
24544ca330bSjmcneill 
24644ca330bSjmcneill 	switch (clk) {
24744ca330bSjmcneill 	case AUDIO_DAI_CLOCK_CBM_CFM:
24844ca330bSjmcneill 		val &= ~AIF1_MSTR_MOD;	/* codec is master */
24944ca330bSjmcneill 		break;
25044ca330bSjmcneill 	case AUDIO_DAI_CLOCK_CBS_CFS:
25144ca330bSjmcneill 		val |= AIF1_MSTR_MOD;	/* codec is slave */
25244ca330bSjmcneill 		break;
25344ca330bSjmcneill 	default:
25444ca330bSjmcneill 		return EINVAL;
25544ca330bSjmcneill 	}
25644ca330bSjmcneill 
25744ca330bSjmcneill 	val &= ~AIF1_LRCK_DIV;
25844ca330bSjmcneill 	val |= __SHIFTIN(AIF1_LRCK_DIV_64, AIF1_LRCK_DIV);
25944ca330bSjmcneill 
26044ca330bSjmcneill 	val &= ~AIF1_BCLK_DIV;
26144ca330bSjmcneill 	val |= __SHIFTIN(AIF1_BCLK_DIV_16, AIF1_BCLK_DIV);
26244ca330bSjmcneill 
26344ca330bSjmcneill 	WR4(sc, AIF1CLK_CTRL, val);
26444ca330bSjmcneill 
26544ca330bSjmcneill 	return 0;
26644ca330bSjmcneill }
26744ca330bSjmcneill 
26876bf467cSjmcneill static int
sun8i_codec_dai_add_device(audio_dai_tag_t dai,audio_dai_tag_t aux)26976bf467cSjmcneill sun8i_codec_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux)
27076bf467cSjmcneill {
27176bf467cSjmcneill 	struct sun8i_codec_softc * const sc = audio_dai_private(dai);
27276bf467cSjmcneill 
27376bf467cSjmcneill 	if (sc->sc_codec_analog != NULL)
27476bf467cSjmcneill 		return 0;
27576bf467cSjmcneill 
27676bf467cSjmcneill 	sc->sc_codec_analog = aux;
27776bf467cSjmcneill 
27876bf467cSjmcneill 	return 0;
27976bf467cSjmcneill }
28076bf467cSjmcneill 
28176bf467cSjmcneill static void
sun8i_codec_set_jackdet(struct sun8i_codec_softc * sc,bool enable)28276bf467cSjmcneill sun8i_codec_set_jackdet(struct sun8i_codec_softc *sc, bool enable)
28376bf467cSjmcneill {
28476bf467cSjmcneill 	const uint32_t mask =
28576bf467cSjmcneill 	    HMIC_CTRL1_JACK_IN_IRQ_EN |
28676bf467cSjmcneill 	    HMIC_CTRL1_JACK_OUT_IRQ_EN |
28776bf467cSjmcneill 	    HMIC_CTRL1_MIC_DET_IRQ_EN;
28876bf467cSjmcneill 	uint32_t val;
28976bf467cSjmcneill 
29076bf467cSjmcneill 	val = RD4(sc, HMIC_CTRL1);
29176bf467cSjmcneill 	if (enable)
29276bf467cSjmcneill 		val |= mask;
29376bf467cSjmcneill 	else
29476bf467cSjmcneill 		val &= ~mask;
29576bf467cSjmcneill 	WR4(sc, HMIC_CTRL1, val);
29676bf467cSjmcneill }
29776bf467cSjmcneill 
29876bf467cSjmcneill static int
sun8i_codec_intr(void * priv)29976bf467cSjmcneill sun8i_codec_intr(void *priv)
30076bf467cSjmcneill {
30176bf467cSjmcneill 	const uint32_t mask =
30276bf467cSjmcneill 	    HMIC_STS_JACK_DET_OIRQ |
30376bf467cSjmcneill 	    HMIC_STS_JACK_DET_IIRQ |
30476bf467cSjmcneill 	    HMIC_STS_MIC_DET_ST;
305fe043784Sjmcneill 	struct sun8i_codec_softc * const sc = priv;
306fe043784Sjmcneill 	uint32_t val;
30776bf467cSjmcneill 
308fe043784Sjmcneill 	val = RD4(sc, HMIC_STS);
309fe043784Sjmcneill 	if (val & mask) {
31076bf467cSjmcneill 		/* Disable jack detect IRQ until work is complete */
31176bf467cSjmcneill 		sun8i_codec_set_jackdet(sc, false);
31276bf467cSjmcneill 
31376bf467cSjmcneill 		/* Schedule pending jack detect task */
31476bf467cSjmcneill 		workqueue_enqueue(sc->sc_workq, &sc->sc_work, NULL);
31576bf467cSjmcneill 	}
31676bf467cSjmcneill 
31776bf467cSjmcneill 	return 1;
31876bf467cSjmcneill }
31976bf467cSjmcneill 
32076bf467cSjmcneill 
32176bf467cSjmcneill static void
sun8i_codec_thread(struct work * wk,void * priv)32276bf467cSjmcneill sun8i_codec_thread(struct work *wk, void *priv)
32376bf467cSjmcneill {
32476bf467cSjmcneill 	struct sun8i_codec_softc * const sc = priv;
32576bf467cSjmcneill 	int hpdet = -1, micdet = -1;
326fe043784Sjmcneill 	uint32_t val;
327fe043784Sjmcneill 
328fe043784Sjmcneill 	val = RD4(sc, HMIC_STS);
32976bf467cSjmcneill 
33076bf467cSjmcneill 	if (sc->sc_codec_analog) {
331fe043784Sjmcneill 		if (val & HMIC_STS_JACK_DET_OIRQ)
33276bf467cSjmcneill 			hpdet = 0 ^ sc->sc_jackdet_pol;
333fe043784Sjmcneill 		else if (val & HMIC_STS_JACK_DET_IIRQ)
33476bf467cSjmcneill 			hpdet = 1 ^ sc->sc_jackdet_pol;
33576bf467cSjmcneill 
336fe043784Sjmcneill 		if (val & HMIC_STS_MIC_DET_ST)
337fe043784Sjmcneill 			micdet = !!(val & HMIC_STS_MIC_PRESENT);
33876bf467cSjmcneill 
33976bf467cSjmcneill 		if (hpdet != -1) {
34076bf467cSjmcneill 			audio_dai_jack_detect(sc->sc_codec_analog,
34176bf467cSjmcneill 			    AUDIO_DAI_JACK_HP, hpdet);
34276bf467cSjmcneill 		}
34376bf467cSjmcneill 		if (micdet != -1) {
34476bf467cSjmcneill 			audio_dai_jack_detect(sc->sc_codec_analog,
34576bf467cSjmcneill 			    AUDIO_DAI_JACK_MIC, micdet);
34676bf467cSjmcneill 		}
34776bf467cSjmcneill 	}
34876bf467cSjmcneill 
349fe043784Sjmcneill 	WR4(sc, HMIC_STS, val);
350fe043784Sjmcneill 
35176bf467cSjmcneill 	/* Re-enable jack detect IRQ */
35276bf467cSjmcneill 	sun8i_codec_set_jackdet(sc, true);
35376bf467cSjmcneill }
35476bf467cSjmcneill 
3556e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
3566e54367aSthorpej 	{ .compat = "allwinner,sun8i-a33-codec" },
3576e54367aSthorpej 	DEVICE_COMPAT_EOL
35844ca330bSjmcneill };
35944ca330bSjmcneill 
36044ca330bSjmcneill static int
sun8i_codec_match(device_t parent,cfdata_t cf,void * aux)36144ca330bSjmcneill sun8i_codec_match(device_t parent, cfdata_t cf, void *aux)
36244ca330bSjmcneill {
36344ca330bSjmcneill 	struct fdt_attach_args * const faa = aux;
36444ca330bSjmcneill 
3656e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
36644ca330bSjmcneill }
36744ca330bSjmcneill 
36844ca330bSjmcneill static void
sun8i_codec_attach(device_t parent,device_t self,void * aux)36944ca330bSjmcneill sun8i_codec_attach(device_t parent, device_t self, void *aux)
37044ca330bSjmcneill {
37144ca330bSjmcneill 	struct sun8i_codec_softc * const sc = device_private(self);
37244ca330bSjmcneill 	struct fdt_attach_args * const faa = aux;
37344ca330bSjmcneill 	const int phandle = faa->faa_phandle;
37476bf467cSjmcneill 	char intrstr[128];
37544ca330bSjmcneill 	bus_addr_t addr;
37644ca330bSjmcneill 	bus_size_t size;
37744ca330bSjmcneill 	uint32_t val;
37876bf467cSjmcneill 	void *ih;
37944ca330bSjmcneill 
38044ca330bSjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
38144ca330bSjmcneill 		aprint_error(": couldn't get registers\n");
38244ca330bSjmcneill 		return;
38344ca330bSjmcneill 	}
38476bf467cSjmcneill 
38576bf467cSjmcneill 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
38676bf467cSjmcneill 		aprint_error(": couldn't decode interrupt\n");
38776bf467cSjmcneill 		return;
38876bf467cSjmcneill 	}
38976bf467cSjmcneill 
39076bf467cSjmcneill 	sc->sc_dev = self;
39144ca330bSjmcneill 	sc->sc_bst = faa->faa_bst;
39244ca330bSjmcneill 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
39344ca330bSjmcneill 		aprint_error(": couldn't map registers\n");
39444ca330bSjmcneill 		return;
39544ca330bSjmcneill 	}
39676bf467cSjmcneill 	sc->sc_jackdet_pol = 1;
39744ca330bSjmcneill 
39844ca330bSjmcneill 	sc->sc_clk_gate = fdtbus_clock_get(phandle, "bus");
39944ca330bSjmcneill 	sc->sc_clk_mod = fdtbus_clock_get(phandle, "mod");
40044ca330bSjmcneill 	if (!sc->sc_clk_gate || !sc->sc_clk_mod) {
40144ca330bSjmcneill 		aprint_error(": couldn't get clocks\n");
40244ca330bSjmcneill 		return;
40344ca330bSjmcneill 	}
40444ca330bSjmcneill 	if (clk_enable(sc->sc_clk_gate) != 0) {
40544ca330bSjmcneill 		aprint_error(": couldn't enable bus clock\n");
40644ca330bSjmcneill 		return;
40744ca330bSjmcneill 	}
40844ca330bSjmcneill 
40944ca330bSjmcneill 	sc->sc_phandle = phandle;
41044ca330bSjmcneill 
41144ca330bSjmcneill 	aprint_naive("\n");
41244ca330bSjmcneill 	aprint_normal(": Audio Codec\n");
41344ca330bSjmcneill 
41476bf467cSjmcneill 	if (workqueue_create(&sc->sc_workq, "jackdet", sun8i_codec_thread,
41576bf467cSjmcneill 	    sc, PRI_NONE, IPL_VM, 0) != 0) {
41676bf467cSjmcneill 		aprint_error_dev(self, "couldn't create jackdet workqueue\n");
41776bf467cSjmcneill 		return;
41876bf467cSjmcneill 	}
41976bf467cSjmcneill 
42044ca330bSjmcneill 	/* Enable clocks */
42144ca330bSjmcneill 	val = RD4(sc, SYSCLK_CTL);
42244ca330bSjmcneill 	val |= AIF1CLK_ENA;
42344ca330bSjmcneill 	val &= ~AIF1CLK_SRC;
42444ca330bSjmcneill 	val |= __SHIFTIN(AIF1CLK_SRC_PLL, AIF1CLK_SRC);
42544ca330bSjmcneill 	val |= SYSCLK_ENA;
42644ca330bSjmcneill 	val &= ~SYSCLK_SRC;
42744ca330bSjmcneill 	WR4(sc, SYSCLK_CTL, val);
42844ca330bSjmcneill 	WR4(sc, MOD_CLK_ENA, MOD_AIF1 | MOD_ADC | MOD_DAC);
42944ca330bSjmcneill 	WR4(sc, MOD_RST_CTL, MOD_AIF1 | MOD_ADC | MOD_DAC);
43044ca330bSjmcneill 
43144ca330bSjmcneill 	/* Enable digital parts */
43244ca330bSjmcneill 	WR4(sc, DAC_DIG_CTRL, DAC_DIG_CTRL_ENDA);
43344ca330bSjmcneill 	WR4(sc, ADC_DIG_CTRL, ADC_DIG_CTRL_ENAD);
43444ca330bSjmcneill 
43544ca330bSjmcneill 	/* Set AIF1 to 48 kHz */
43644ca330bSjmcneill 	val = RD4(sc, SYS_SR_CTRL);
43744ca330bSjmcneill 	val &= ~AIF1_FS;
43844ca330bSjmcneill 	val |= __SHIFTIN(AIF_FS_48KHZ, AIF1_FS);
43944ca330bSjmcneill 	WR4(sc, SYS_SR_CTRL, val);
44044ca330bSjmcneill 
44144ca330bSjmcneill 	/* Set AIF1 to 16-bit */
44244ca330bSjmcneill 	val = RD4(sc, AIF1CLK_CTRL);
44344ca330bSjmcneill 	val &= ~AIF1_WORD_SIZ;
44444ca330bSjmcneill 	val |= __SHIFTIN(AIF1_WORD_SIZ_16, AIF1_WORD_SIZ);
44544ca330bSjmcneill 	WR4(sc, AIF1CLK_CTRL, val);
44644ca330bSjmcneill 
44744ca330bSjmcneill 	/* Enable AIF1 DAC timelot 0 */
44844ca330bSjmcneill 	val = RD4(sc, AIF1_DACDAT_CTRL);
44944ca330bSjmcneill 	val |= AIF1_DAC0L_ENA;
45044ca330bSjmcneill 	val |= AIF1_DAC0R_ENA;
45144ca330bSjmcneill 	WR4(sc, AIF1_DACDAT_CTRL, val);
45244ca330bSjmcneill 
45344ca330bSjmcneill 	/* DAC mixer source select */
45444ca330bSjmcneill 	val = RD4(sc, DAC_MXR_SRC);
45544ca330bSjmcneill 	val &= ~DACL_MXR_SRC;
45644ca330bSjmcneill 	val |= __SHIFTIN(DACL_MXR_SRC_AIF1_DAC0L, DACL_MXR_SRC);
45744ca330bSjmcneill 	val &= ~DACR_MXR_SRC;
45844ca330bSjmcneill 	val |= __SHIFTIN(DACR_MXR_SRC_AIF1_DAC0R, DACR_MXR_SRC);
45944ca330bSjmcneill 	WR4(sc, DAC_MXR_SRC, val);
46044ca330bSjmcneill 
46144ca330bSjmcneill 	/* Enable PA power */
46244ca330bSjmcneill 	sc->sc_pin_pa = fdtbus_gpio_acquire(phandle, "allwinner,pa-gpios", GPIO_PIN_OUTPUT);
46344ca330bSjmcneill 	if (sc->sc_pin_pa)
46444ca330bSjmcneill 		fdtbus_gpio_write(sc->sc_pin_pa, 1);
46544ca330bSjmcneill 
46676bf467cSjmcneill 	/* Enable jack detect */
46776bf467cSjmcneill 	val = RD4(sc, HMIC_CTRL1);
46876bf467cSjmcneill 	val |= __SHIFTIN(0xff, HMIC_CTRL1_N);
46976bf467cSjmcneill 	WR4(sc, HMIC_CTRL1, val);
47076bf467cSjmcneill 
47176bf467cSjmcneill 	val = RD4(sc, HMIC_CTRL2);
47276bf467cSjmcneill 	val &= ~HMIC_CTRL2_MDATA_THRES;
47376bf467cSjmcneill 	val |= __SHIFTIN(0x17, HMIC_CTRL2_MDATA_THRES);
47476bf467cSjmcneill 	WR4(sc, HMIC_CTRL2, val);
47576bf467cSjmcneill 
47676bf467cSjmcneill 	/* Schedule initial jack detect task */
47776bf467cSjmcneill 	workqueue_enqueue(sc->sc_workq, &sc->sc_work, NULL);
47876bf467cSjmcneill 
479076a1169Sjmcneill 	ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, FDT_INTR_MPSAFE,
480076a1169Sjmcneill 	    sun8i_codec_intr, sc, device_xname(self));
48176bf467cSjmcneill 	if (ih == NULL) {
48276bf467cSjmcneill 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
48376bf467cSjmcneill 		    intrstr);
48476bf467cSjmcneill 		return;
48576bf467cSjmcneill 	}
48676bf467cSjmcneill 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
48776bf467cSjmcneill 
48844ca330bSjmcneill 	sc->sc_dai.dai_set_format = sun8i_codec_dai_set_format;
48976bf467cSjmcneill 	sc->sc_dai.dai_add_device = sun8i_codec_dai_add_device;
49044ca330bSjmcneill 	sc->sc_dai.dai_hw_if = &sun8i_codec_hw_if;
49144ca330bSjmcneill 	sc->sc_dai.dai_dev = self;
49244ca330bSjmcneill 	sc->sc_dai.dai_priv = sc;
49344ca330bSjmcneill 	fdtbus_register_dai_controller(self, phandle, &sun8i_codec_dai_funcs);
49444ca330bSjmcneill }
49544ca330bSjmcneill 
49644ca330bSjmcneill CFATTACH_DECL_NEW(sun8i_codec, sizeof(struct sun8i_codec_softc),
49744ca330bSjmcneill     sun8i_codec_match, sun8i_codec_attach, NULL, NULL);
498