1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ALSA driver for ICEnsemble ICE1712 (Envy24) |
4 | * |
5 | * AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface |
6 | * |
7 | * Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz> |
8 | */ |
9 | |
10 | #include <linux/io.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/init.h> |
15 | #include <linux/module.h> |
16 | #include <sound/core.h> |
17 | #include <sound/initval.h> |
18 | #include "ice1712.h" |
19 | |
20 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>" ); |
21 | MODULE_DESCRIPTION("ICEnsemble ICE17xx <-> AK4xxx AD/DA chip interface" ); |
22 | MODULE_LICENSE("GPL" ); |
23 | |
24 | static void snd_ice1712_akm4xxx_lock(struct snd_akm4xxx *ak, int chip) |
25 | { |
26 | struct snd_ice1712 *ice = ak->private_data[0]; |
27 | |
28 | snd_ice1712_save_gpio_status(ice); |
29 | } |
30 | |
31 | static void snd_ice1712_akm4xxx_unlock(struct snd_akm4xxx *ak, int chip) |
32 | { |
33 | struct snd_ice1712 *ice = ak->private_data[0]; |
34 | |
35 | snd_ice1712_restore_gpio_status(ice); |
36 | } |
37 | |
38 | /* |
39 | * write AK4xxx register |
40 | */ |
41 | static void snd_ice1712_akm4xxx_write(struct snd_akm4xxx *ak, int chip, |
42 | unsigned char addr, unsigned char data) |
43 | { |
44 | unsigned int tmp; |
45 | int idx; |
46 | unsigned int addrdata; |
47 | struct snd_ak4xxx_private *priv = (void *)ak->private_value[0]; |
48 | struct snd_ice1712 *ice = ak->private_data[0]; |
49 | |
50 | if (snd_BUG_ON(chip < 0 || chip >= 4)) |
51 | return; |
52 | |
53 | tmp = snd_ice1712_gpio_read(ice); |
54 | tmp |= priv->add_flags; |
55 | tmp &= ~priv->mask_flags; |
56 | if (priv->cs_mask == priv->cs_addr) { |
57 | if (priv->cif) { |
58 | tmp |= priv->cs_mask; /* start without chip select */ |
59 | } else { |
60 | tmp &= ~priv->cs_mask; /* chip select low */ |
61 | snd_ice1712_gpio_write(ice, val: tmp); |
62 | udelay(1); |
63 | } |
64 | } else { |
65 | /* doesn't handle cf=1 yet */ |
66 | tmp &= ~priv->cs_mask; |
67 | tmp |= priv->cs_addr; |
68 | snd_ice1712_gpio_write(ice, val: tmp); |
69 | udelay(1); |
70 | } |
71 | |
72 | /* build I2C address + data byte */ |
73 | addrdata = (priv->caddr << 6) | 0x20 | (addr & 0x1f); |
74 | addrdata = (addrdata << 8) | data; |
75 | for (idx = 15; idx >= 0; idx--) { |
76 | /* drop clock */ |
77 | tmp &= ~priv->clk_mask; |
78 | snd_ice1712_gpio_write(ice, val: tmp); |
79 | udelay(1); |
80 | /* set data */ |
81 | if (addrdata & (1 << idx)) |
82 | tmp |= priv->data_mask; |
83 | else |
84 | tmp &= ~priv->data_mask; |
85 | snd_ice1712_gpio_write(ice, val: tmp); |
86 | udelay(1); |
87 | /* raise clock */ |
88 | tmp |= priv->clk_mask; |
89 | snd_ice1712_gpio_write(ice, val: tmp); |
90 | udelay(1); |
91 | } |
92 | |
93 | if (priv->cs_mask == priv->cs_addr) { |
94 | if (priv->cif) { |
95 | /* assert a cs pulse to trigger */ |
96 | tmp &= ~priv->cs_mask; |
97 | snd_ice1712_gpio_write(ice, val: tmp); |
98 | udelay(1); |
99 | } |
100 | tmp |= priv->cs_mask; /* chip select high to trigger */ |
101 | } else { |
102 | tmp &= ~priv->cs_mask; |
103 | tmp |= priv->cs_none; /* deselect address */ |
104 | } |
105 | snd_ice1712_gpio_write(ice, val: tmp); |
106 | udelay(1); |
107 | } |
108 | |
109 | /* |
110 | * initialize the struct snd_akm4xxx record with the template |
111 | */ |
112 | int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *temp, |
113 | const struct snd_ak4xxx_private *_priv, struct snd_ice1712 *ice) |
114 | { |
115 | struct snd_ak4xxx_private *priv; |
116 | |
117 | if (_priv != NULL) { |
118 | priv = kmalloc(size: sizeof(*priv), GFP_KERNEL); |
119 | if (priv == NULL) |
120 | return -ENOMEM; |
121 | *priv = *_priv; |
122 | } else { |
123 | priv = NULL; |
124 | } |
125 | *ak = *temp; |
126 | ak->card = ice->card; |
127 | ak->private_value[0] = (unsigned long)priv; |
128 | ak->private_data[0] = ice; |
129 | if (ak->ops.lock == NULL) |
130 | ak->ops.lock = snd_ice1712_akm4xxx_lock; |
131 | if (ak->ops.unlock == NULL) |
132 | ak->ops.unlock = snd_ice1712_akm4xxx_unlock; |
133 | if (ak->ops.write == NULL) |
134 | ak->ops.write = snd_ice1712_akm4xxx_write; |
135 | snd_akm4xxx_init(ak); |
136 | return 0; |
137 | } |
138 | |
139 | void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice) |
140 | { |
141 | unsigned int akidx; |
142 | if (ice->akm == NULL) |
143 | return; |
144 | for (akidx = 0; akidx < ice->akm_codecs; akidx++) { |
145 | struct snd_akm4xxx *ak = &ice->akm[akidx]; |
146 | kfree(objp: (void*)ak->private_value[0]); |
147 | } |
148 | kfree(objp: ice->akm); |
149 | } |
150 | |
151 | /* |
152 | * build AK4xxx controls |
153 | */ |
154 | int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice) |
155 | { |
156 | unsigned int akidx; |
157 | int err; |
158 | |
159 | for (akidx = 0; akidx < ice->akm_codecs; akidx++) { |
160 | struct snd_akm4xxx *ak = &ice->akm[akidx]; |
161 | err = snd_akm4xxx_build_controls(ak); |
162 | if (err < 0) |
163 | return err; |
164 | } |
165 | return 0; |
166 | } |
167 | |
168 | EXPORT_SYMBOL(snd_ice1712_akm4xxx_init); |
169 | EXPORT_SYMBOL(snd_ice1712_akm4xxx_free); |
170 | EXPORT_SYMBOL(snd_ice1712_akm4xxx_build_controls); |
171 | |