Skip to content

Commit ba36bf6

Browse files
Silicon-Signalsdkalowsk
authored andcommitted
audio: codec: Add driver for MAX98091 codec
This patch adds a minimal driver for the MAX98091 audio codec. Currently, playback functionality is supported. Co-developed-by: Rutvij Trivedi <rutvij.trivedi@siliconsignals.io> Signed-off-by: Rutvij Trivedi <rutvij.trivedi@siliconsignals.io> Co-developed-by: Tarang Raval <tarang.raval@siliconsignals.io> Signed-off-by: Tarang Raval <tarang.raval@siliconsignals.io> Signed-off-by: Silicon Signals <siliconsignalsforgit@gmail.com>
1 parent 6231b43 commit ba36bf6

File tree

6 files changed

+567
-0
lines changed

6 files changed

+567
-0
lines changed

drivers/audio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_WM8904 wm8904.c)
1414
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_WM8962 wm8962.c)
1515
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_CS43L22 cs43l22.c)
1616
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_PCM1681 pcm1681.c)
17+
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_MAX98091 max98091.c)

drivers/audio/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module-str = audio codec
3636
source "subsys/logging/Kconfig.template.log_config"
3737

3838
source "drivers/audio/Kconfig.cs43l22"
39+
source "drivers/audio/Kconfig.max98091"
3940
source "drivers/audio/Kconfig.pcm1681"
4041
source "drivers/audio/Kconfig.tas6422dac"
4142
source "drivers/audio/Kconfig.tlv320aic3110"

drivers/audio/Kconfig.max98091

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2025 Silicon Signals Pvt. Ltd.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config AUDIO_CODEC_MAX98091
5+
bool "Maxim MAX98091 codec support"
6+
default y
7+
select I2C
8+
depends on DT_HAS_MAXIM_MAX98091_ENABLED
9+
help
10+
Enable support for the MAX98091 I2S codec via I2C.

drivers/audio/max98091.c

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Copyright (c) 2025 Silicon Signals Pvt. Ltd.
5+
* Author: Rutvij Trivedi <rutvij.trivedi@siliconsignals.io>
6+
* Author: Tarang Raval <tarang.raval@siliconsignals.io>
7+
*/
8+
9+
#include <zephyr/drivers/i2c.h>
10+
#include <zephyr/audio/codec.h>
11+
#include <zephyr/device.h>
12+
#include <zephyr/logging/log.h>
13+
#include "max98091.h"
14+
15+
LOG_MODULE_REGISTER(maxim_max98091);
16+
17+
#define DT_DRV_COMPAT maxim_max98091
18+
19+
struct max98091_config {
20+
struct i2c_dt_spec i2c;
21+
uint32_t mclk_freq;
22+
};
23+
24+
static void max98091_write_reg(const struct device *dev, uint8_t reg, uint8_t val)
25+
{
26+
const struct max98091_config *const dev_cfg = dev->config;
27+
28+
i2c_reg_write_byte_dt(&dev_cfg->i2c, reg, val);
29+
}
30+
31+
static void max98091_read_reg(const struct device *dev, uint8_t reg, uint8_t *val)
32+
{
33+
const struct max98091_config *const dev_cfg = dev->config;
34+
35+
i2c_reg_read_byte_dt(&dev_cfg->i2c, reg, val);
36+
}
37+
38+
static void max98091_update_reg(const struct device *dev, uint8_t reg, uint8_t mask, uint8_t val)
39+
{
40+
const struct max98091_config *const dev_cfg = dev->config;
41+
42+
i2c_reg_update_byte_dt(&dev_cfg->i2c, reg, mask, val);
43+
}
44+
45+
static void max98091_soft_reset(const struct device *dev)
46+
{
47+
max98091_write_reg(dev, M98091_REG_SOFTWARE_RESET, 0x01);
48+
k_msleep(20);
49+
}
50+
51+
/* Configuration Functions */
52+
static int max98091_protocol_config(const struct device *dev, audio_dai_type_t dai_type)
53+
{
54+
uint8_t fmt_reg = 0;
55+
56+
switch (dai_type) {
57+
case AUDIO_DAI_TYPE_I2S:
58+
fmt_reg |= M98091_I2S_S_MASK;
59+
break;
60+
case AUDIO_DAI_TYPE_LEFT_JUSTIFIED:
61+
fmt_reg |= M98091_LJ_S_MASK;
62+
break;
63+
case AUDIO_DAI_TYPE_RIGHT_JUSTIFIED:
64+
fmt_reg |= M98091_RJ_S_MASK;
65+
break;
66+
default:
67+
LOG_ERR("Unsupported DAI type: %d", dai_type);
68+
return -EINVAL;
69+
}
70+
max98091_write_reg(dev, M98091_REG_DAI_INTERFACE, fmt_reg);
71+
LOG_DBG("Protocol configured: 0x%02x", fmt_reg);
72+
return 0;
73+
}
74+
75+
static int max98091_audio_fmt_config(const struct device *dev, audio_dai_cfg_t *cfg)
76+
{
77+
uint8_t sample_rate;
78+
uint8_t channels;
79+
uint8_t word_size;
80+
81+
switch (cfg->i2s.frame_clk_freq) {
82+
case 8000:
83+
sample_rate = M98091_SR_8K_MASK;
84+
break;
85+
case 16000:
86+
sample_rate = M98091_SR_16K_MASK;
87+
break;
88+
case 32000:
89+
sample_rate = M98091_SR_32K_MASK;
90+
break;
91+
case 44100:
92+
sample_rate = M98091_SR_44K1_MASK;
93+
break;
94+
case 48000:
95+
sample_rate = M98091_SR_48K_MASK;
96+
break;
97+
case 96000:
98+
sample_rate = M98091_SR_96K_MASK;
99+
break;
100+
default:
101+
LOG_ERR("Unsupported sample rate: %d", cfg->i2s.frame_clk_freq);
102+
return -EINVAL;
103+
}
104+
105+
max98091_write_reg(dev, M98091_REG_QUICK_SAMPLE_RATE, sample_rate);
106+
107+
switch (cfg->i2s.channels) {
108+
case 1: /* Mono */
109+
channels = 1;
110+
break;
111+
case 2: /* Stereo */
112+
channels = 0;
113+
break;
114+
default:
115+
LOG_ERR("Unsupported channels: %d", cfg->i2s.channels);
116+
return -EINVAL;
117+
}
118+
max98091_update_reg(dev, M98091_REG_IO_CONFIGURATION, M98091_DMONO_MASK, channels);
119+
120+
switch (cfg->i2s.word_size) {
121+
case 16:
122+
word_size = M98091_16B_WS;
123+
break;
124+
default:
125+
LOG_ERR("Word size %d bits not supported; falling back to 16 bits",
126+
cfg->i2s.word_size);
127+
word_size = M98091_16B_WS;
128+
break;
129+
}
130+
max98091_update_reg(dev, M98091_REG_INTERFACE_FORMAT, M98091_WS_MASK, word_size);
131+
132+
return 0;
133+
}
134+
135+
static void max98091_set_system_clock(const struct device *dev, uint32_t mclk_freq)
136+
{
137+
uint8_t psclk;
138+
139+
if (mclk_freq >= 10000000 && mclk_freq <= 20000000) {
140+
psclk = M98091_PSCLK_DIV1;
141+
} else if (mclk_freq > 20000000 && mclk_freq <= 40000000) {
142+
psclk = M98091_PSCLK_DIV2;
143+
} else if (mclk_freq > 40000000 && mclk_freq <= 60000000) {
144+
psclk = M98091_PSCLK_DIV4;
145+
} else {
146+
LOG_ERR("Invalid MCLK frequency: %u", mclk_freq);
147+
return;
148+
}
149+
max98091_write_reg(dev, M98091_REG_SYSTEM_CLOCK, psclk);
150+
LOG_DBG("System clock set: PSCLK=0x%02x", psclk);
151+
152+
max98091_update_reg(dev, M98091_REG_MASTER_MODE, M98091_MAS_MASK, 0);
153+
}
154+
155+
static int max98091_set_volume_or_mute(const struct device *dev, audio_channel_t channel, int value,
156+
bool is_volume)
157+
{
158+
uint8_t hp_mask = is_volume ? M98091_HPVOLL_MASK : M98091_HPLM_MASK;
159+
uint8_t spk_mask = is_volume ? M98091_SPVOLL_MASK : M98091_SPLM_MASK;
160+
161+
switch (channel) {
162+
case AUDIO_CHANNEL_FRONT_LEFT:
163+
max98091_update_reg(dev, M98091_REG_LEFT_SPK_VOLUME, spk_mask, value);
164+
return 0;
165+
166+
case AUDIO_CHANNEL_FRONT_RIGHT:
167+
max98091_update_reg(dev, M98091_REG_RIGHT_SPK_VOLUME, spk_mask, value);
168+
return 0;
169+
170+
case AUDIO_CHANNEL_HEADPHONE_LEFT:
171+
max98091_update_reg(dev, M98091_REG_LEFT_HP_VOLUME, hp_mask, value);
172+
return 0;
173+
174+
case AUDIO_CHANNEL_HEADPHONE_RIGHT:
175+
max98091_update_reg(dev, M98091_REG_RIGHT_HP_VOLUME, hp_mask, value);
176+
return 0;
177+
178+
case AUDIO_CHANNEL_ALL:
179+
max98091_update_reg(dev, M98091_REG_LEFT_SPK_VOLUME, spk_mask, value);
180+
max98091_update_reg(dev, M98091_REG_RIGHT_SPK_VOLUME, spk_mask, value);
181+
max98091_update_reg(dev, M98091_REG_LEFT_HP_VOLUME, hp_mask, value);
182+
max98091_update_reg(dev, M98091_REG_RIGHT_HP_VOLUME, hp_mask, value);
183+
return 0;
184+
185+
default:
186+
return -EINVAL;
187+
}
188+
}
189+
190+
static int max98091_out_volume_config(const struct device *dev, audio_channel_t channel, int volume)
191+
{
192+
return max98091_set_volume_or_mute(dev, channel, volume, true);
193+
}
194+
195+
static int max98091_out_mute_config(const struct device *dev, audio_channel_t channel, bool mute)
196+
{
197+
return max98091_set_volume_or_mute(dev, channel, mute, false);
198+
}
199+
200+
/* Audio Path Configuration */
201+
static void max98091_configure_output(const struct device *dev)
202+
{
203+
max98091_update_reg(dev, M98091_REG_IO_CONFIGURATION, M98091_SDIEN_MASK, M98091_SDIEN_MASK);
204+
205+
max98091_write_reg(dev, M98091_REG_LEFT_SPK_MIXER, M98091_MIXSPL_DACL_MASK);
206+
max98091_write_reg(dev, M98091_REG_RIGHT_SPK_MIXER, M98091_MIXSPR_DACR_MASK);
207+
208+
/* select DAC only source */
209+
max98091_write_reg(dev, M98091_REG_HP_CONTROL, 0x00);
210+
211+
/* M98091_HPREN_MASK, M98091_HPLEN_MASK, M98091_SPREN_MASK,
212+
* M98091_SPLEN_MASK, M98091_DAREN_MASK, M98091_DALEN_MASK
213+
*/
214+
max98091_write_reg(dev, M98091_REG_OUTPUT_ENABLE, 0xf3);
215+
216+
max98091_out_volume_config(dev, AUDIO_CHANNEL_ALL, M98091_DEFAULT_VOLUME);
217+
max98091_out_mute_config(dev, AUDIO_CHANNEL_ALL, false);
218+
}
219+
220+
static void max98091_start_output(const struct device *dev)
221+
{
222+
ARG_UNUSED(dev);
223+
}
224+
225+
static void max98091_stop_output(const struct device *dev)
226+
{
227+
ARG_UNUSED(dev);
228+
}
229+
230+
static int max98091_set_property(const struct device *dev, audio_property_t property,
231+
audio_channel_t channel, audio_property_value_t val)
232+
{
233+
switch (property) {
234+
case AUDIO_PROPERTY_OUTPUT_VOLUME:
235+
max98091_out_volume_config(dev, channel, val.vol);
236+
case AUDIO_PROPERTY_OUTPUT_MUTE:
237+
max98091_out_mute_config(dev, channel, val.mute);
238+
default:
239+
return -EINVAL;
240+
}
241+
}
242+
243+
static int max98091_configure(const struct device *dev, struct audio_codec_cfg *cfg)
244+
{
245+
const struct max98091_config *const dev_cfg = dev->config;
246+
247+
if (cfg->dai_type >= AUDIO_DAI_TYPE_INVALID) {
248+
LOG_ERR("dai_type not supported");
249+
return -EINVAL;
250+
}
251+
252+
max98091_soft_reset(dev);
253+
254+
if (cfg->dai_route == AUDIO_ROUTE_BYPASS) {
255+
return 0;
256+
}
257+
258+
/* Put the audio codec into shutdown mode */
259+
max98091_write_reg(dev, M98091_REG_DEVICE_SHUTDOWN, 0x00);
260+
261+
max98091_write_reg(dev, M98091_REG_DAC_CONTROL, 0x00);
262+
263+
max98091_write_reg(dev, M98091_REG_TDM_CONTROL, 0x00);
264+
265+
/* Set DLY = 1 to conform to the I2S standard.
266+
* DLY is only effective when TDM = 0
267+
*/
268+
max98091_write_reg(dev, M98091_REG_INTERFACE_FORMAT, M98091_DLY_MASK);
269+
270+
/* Configure system clock */
271+
max98091_set_system_clock(dev, dev_cfg->mclk_freq);
272+
273+
max98091_protocol_config(dev, cfg->dai_type);
274+
max98091_audio_fmt_config(dev, &cfg->dai_cfg);
275+
276+
/* Configure audio paths based on route */
277+
switch (cfg->dai_route) {
278+
case AUDIO_ROUTE_PLAYBACK:
279+
max98091_configure_output(dev);
280+
break;
281+
default:
282+
LOG_DBG("Unsupported audio route selected");
283+
break;
284+
}
285+
286+
/* Bring the audio codec out of shutdown mode */
287+
max98091_write_reg(dev, M98091_REG_DEVICE_SHUTDOWN, M98091_SHDNN_MASK);
288+
289+
return 0;
290+
}
291+
292+
static const struct audio_codec_api max98091_api = {
293+
.configure = max98091_configure,
294+
.start_output = max98091_start_output,
295+
.stop_output = max98091_stop_output,
296+
.set_property = max98091_set_property,
297+
};
298+
299+
static int max98091_init(const struct device *dev)
300+
{
301+
const struct max98091_config *cfg_tan = dev->config;
302+
uint8_t device_id;
303+
304+
if (!i2c_is_ready_dt(&cfg_tan->i2c)) {
305+
LOG_ERR("I2C bus not ready");
306+
return -ENODEV;
307+
}
308+
309+
max98091_read_reg(dev, M98091_REG_REVISION_ID, &device_id);
310+
if (device_id >= M98091_REVA && (device_id <= M98091_REVA + 0x0f)) {
311+
LOG_INF("MAX98091 Device ID: 0x%02X", device_id);
312+
return 0;
313+
}
314+
315+
LOG_ERR("Invalid MAX98091 Device ID: 0x%02X", device_id);
316+
return -EINVAL;
317+
}
318+
319+
#define MAX98091_INIT(inst) \
320+
static const struct max98091_config max98091_config_##inst = { \
321+
.i2c = I2C_DT_SPEC_INST_GET(inst), \
322+
.mclk_freq = DT_INST_PROP(inst, mclk_frequency)}; \
323+
DEVICE_DT_INST_DEFINE(inst, max98091_init, NULL, NULL, &max98091_config_##inst, \
324+
POST_KERNEL, CONFIG_AUDIO_CODEC_INIT_PRIORITY, &max98091_api);
325+
326+
DT_INST_FOREACH_STATUS_OKAY(MAX98091_INIT)

0 commit comments

Comments
 (0)