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