xref: /btstack/port/stm32-f4discovery-usb/bsp/cs43l22.c (revision f763b8ceaf9411e1c5e999bc4423cdc8accb9a47)
1 /**
2   ******************************************************************************
3   * @file    cs43l22.c
4   * @author  MCD Application Team
5   * @version V2.0.2
6   * @date    06-October-2015
7   * @brief   This file provides the CS43L22 Audio Codec driver.
8   ******************************************************************************
9   * @attention
10   *
11   * <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
12   *
13   * Redistribution and use in source and binary forms, with or without modification,
14   * are permitted provided that the following conditions are met:
15   *   1. Redistributions of source code must retain the above copyright notice,
16   *      this list of conditions and the following disclaimer.
17   *   2. Redistributions in binary form must reproduce the above copyright notice,
18   *      this list of conditions and the following disclaimer in the documentation
19   *      and/or other materials provided with the distribution.
20   *   3. Neither the name of STMicroelectronics nor the names of its contributors
21   *      may be used to endorse or promote products derived from this software
22   *      without specific prior written permission.
23   *
24   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34   *
35   ******************************************************************************
36   */
37 
38 /* Includes ------------------------------------------------------------------*/
39 #include "cs43l22.h"
40 
41 /** @addtogroup BSP
42   * @{
43   */
44 
45 /** @addtogroup Components
46   * @{
47   */
48 
49 /** @addtogroup CS43L22
50   * @brief     This file provides a set of functions needed to drive the
51   *            CS43L22 audio codec.
52   * @{
53   */
54 
55 /** @defgroup CS43L22_Private_Types
56   * @{
57   */
58 
59 /**
60   * @}
61   */
62 
63 /** @defgroup CS43L22_Private_Defines
64   * @{
65   */
66 #define VOLUME_CONVERT(Volume)    (((Volume) > 100)? 100:((uint8_t)(((Volume) * 255) / 100)))
67 /* Uncomment this line to enable verifying data sent to codec after each write
68    operation (for debug purpose) */
69 #if !defined (VERIFY_WRITTENDATA)
70 /* #define VERIFY_WRITTENDATA */
71 #endif /* VERIFY_WRITTENDATA */
72 /**
73   * @}
74   */
75 
76 /** @defgroup CS43L22_Private_Macros
77   * @{
78   */
79 
80 /**
81   * @}
82   */
83 
84 /** @defgroup CS43L22_Private_Variables
85   * @{
86   */
87 
88 /* Audio codec driver structure initialization */
89 AUDIO_DrvTypeDef cs43l22_drv =
90 {
91   cs43l22_Init,
92   cs43l22_DeInit,
93   cs43l22_ReadID,
94 
95   cs43l22_Play,
96   cs43l22_Pause,
97   cs43l22_Resume,
98   cs43l22_Stop,
99 
100   cs43l22_SetFrequency,
101   cs43l22_SetVolume,
102   cs43l22_SetMute,
103   cs43l22_SetOutputMode,
104   cs43l22_Reset,
105 };
106 
107 static uint8_t Is_cs43l22_Stop = 1;
108 
109 volatile uint8_t OutputDev = 0;
110 
111 /**
112   * @}
113   */
114 
115 /** @defgroup CS43L22_Function_Prototypes
116   * @{
117   */
118 static uint8_t CODEC_IO_Write(uint8_t Addr, uint8_t Reg, uint8_t Value);
119 /**
120   * @}
121   */
122 
123 /** @defgroup CS43L22_Private_Functions
124   * @{
125   */
126 
127 /**
128   * @brief Initializes the audio codec and the control interface.
129   * @param DeviceAddr: Device address on communication Bus.
130   * @param OutputDevice: can be OUTPUT_DEVICE_SPEAKER, OUTPUT_DEVICE_HEADPHONE,
131   *                       OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO .
132   * @param Volume: Initial volume level (from 0 (Mute) to 100 (Max))
133   * @retval 0 if correct communication, else wrong communication
134   */
cs43l22_Init(uint16_t DeviceAddr,uint16_t OutputDevice,uint8_t Volume,uint32_t AudioFreq)135 uint32_t cs43l22_Init(uint16_t DeviceAddr, uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq)
136 {
137   uint32_t counter = 0;
138 
139   /* Initialize the Control interface of the Audio Codec */
140   AUDIO_IO_Init();
141 
142   /* Keep Codec powered OFF */
143   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL1, 0x01);
144 
145   /*Save Output device for mute ON/OFF procedure*/
146   switch (OutputDevice)
147   {
148   case OUTPUT_DEVICE_SPEAKER:
149     OutputDev = 0xFA;
150     break;
151 
152   case OUTPUT_DEVICE_HEADPHONE:
153     OutputDev = 0xAF;
154     break;
155 
156   case OUTPUT_DEVICE_BOTH:
157     OutputDev = 0xAA;
158     break;
159 
160   case OUTPUT_DEVICE_AUTO:
161     OutputDev = 0x05;
162     break;
163 
164   default:
165     OutputDev = 0x05;
166     break;
167   }
168 
169   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL2, OutputDev);
170 
171   /* Clock configuration: Auto detection */
172   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_CLOCKING_CTL, 0x81);
173 
174   /* Set the Slave Mode and the audio Standard */
175   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_INTERFACE_CTL1, CODEC_STANDARD);
176 
177   /* Set the Master volume */
178   counter += cs43l22_SetVolume(DeviceAddr, Volume);
179 
180   /* If the Speaker is enabled, set the Mono mode and volume attenuation level */
181   if(OutputDevice != OUTPUT_DEVICE_HEADPHONE)
182   {
183     /* Set the Speaker Mono mode */
184     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_PLAYBACK_CTL2, 0x06);
185 
186     /* Set the Speaker attenuation level */
187     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_SPEAKER_A_VOL, 0x00);
188     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_SPEAKER_B_VOL, 0x00);
189   }
190 
191   /* Additional configuration for the CODEC. These configurations are done to reduce
192   the time needed for the Codec to power off. If these configurations are removed,
193   then a long delay should be added between powering off the Codec and switching
194   off the I2S peripheral MCLK clock (which is the operating clock for Codec).
195   If this delay is not inserted, then the codec will not shut down properly and
196   it results in high noise after shut down. */
197 
198   /* Disable the analog soft ramp */
199   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_ANALOG_ZC_SR_SETT, 0x00);
200   /* Disable the digital soft ramp */
201   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_MISC_CTL, 0x04);
202   /* Disable the limiter attack level */
203   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_LIMIT_CTL1, 0x00);
204   /* Adjust Bass and Treble levels */
205   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_TONE_CTL, 0x0F);
206   /* Adjust PCM volume level */
207   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_PCMA_VOL, 0x0A);
208   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_PCMB_VOL, 0x0A);
209 
210   /* Return communication control value */
211   return counter;
212 }
213 
214 /**
215   * @brief  Deinitializes the audio codec.
216   * @param  None
217   * @retval  None
218   */
cs43l22_DeInit(void)219 void cs43l22_DeInit(void)
220 {
221   /* Deinitialize Audio Codec interface */
222   AUDIO_IO_DeInit();
223 }
224 
225 /**
226   * @brief  Get the CS43L22 ID.
227   * @param DeviceAddr: Device address on communication Bus.
228   * @retval The CS43L22 ID
229   */
cs43l22_ReadID(uint16_t DeviceAddr)230 uint32_t cs43l22_ReadID(uint16_t DeviceAddr)
231 {
232   uint8_t Value;
233   /* Initialize the Control interface of the Audio Codec */
234   AUDIO_IO_Init();
235 
236   Value = AUDIO_IO_Read(DeviceAddr, CS43L22_CHIPID_ADDR);
237   Value = (Value & CS43L22_ID_MASK);
238 
239   return((uint32_t) Value);
240 }
241 
242 /**
243   * @brief Start the audio Codec play feature.
244   * @note For this codec no Play options are required.
245   * @param DeviceAddr: Device address on communication Bus.
246   * @retval 0 if correct communication, else wrong communication
247   */
cs43l22_Play(uint16_t DeviceAddr,uint16_t * pBuffer,uint16_t Size)248 uint32_t cs43l22_Play(uint16_t DeviceAddr, uint16_t* pBuffer, uint16_t Size)
249 {
250   uint32_t counter = 0;
251 
252   if(Is_cs43l22_Stop == 1)
253   {
254     /* Enable the digital soft ramp */
255     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_MISC_CTL, 0x06);
256 
257     /* Enable Output device */
258     counter += cs43l22_SetMute(DeviceAddr, AUDIO_MUTE_OFF);
259 
260     /* Power on the Codec */
261     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL1, 0x9E);
262     Is_cs43l22_Stop = 0;
263   }
264 
265   /* Return communication control value */
266   return counter;
267 }
268 
269 /**
270   * @brief Pauses playing on the audio codec.
271   * @param DeviceAddr: Device address on communication Bus.
272   * @retval 0 if correct communication, else wrong communication
273   */
cs43l22_Pause(uint16_t DeviceAddr)274 uint32_t cs43l22_Pause(uint16_t DeviceAddr)
275 {
276   uint32_t counter = 0;
277 
278   /* Pause the audio file playing */
279   /* Mute the output first */
280   counter += cs43l22_SetMute(DeviceAddr, AUDIO_MUTE_ON);
281 
282   /* Put the Codec in Power save mode */
283   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL1, 0x01);
284 
285   return counter;
286 }
287 
288 /**
289   * @brief Resumes playing on the audio codec.
290   * @param DeviceAddr: Device address on communication Bus.
291   * @retval 0 if correct communication, else wrong communication
292   */
cs43l22_Resume(uint16_t DeviceAddr)293 uint32_t cs43l22_Resume(uint16_t DeviceAddr)
294 {
295   uint32_t counter = 0;
296   volatile uint32_t index = 0x00;
297   /* Resumes the audio file playing */
298   /* Unmute the output first */
299   counter += cs43l22_SetMute(DeviceAddr, AUDIO_MUTE_OFF);
300 
301   for(index = 0x00; index < 0xFF; index++);
302 
303   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL2, OutputDev);
304 
305   /* Exit the Power save mode */
306   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL1, 0x9E);
307 
308   return counter;
309 }
310 
311 /**
312   * @brief Stops audio Codec playing. It powers down the codec.
313   * @param DeviceAddr: Device address on communication Bus.
314   * @param CodecPdwnMode: selects the  power down mode.
315   *          - CODEC_PDWN_HW: Physically power down the codec. When resuming from this
316   *                           mode, the codec is set to default configuration
317   *                           (user should re-Initialize the codec in order to
318   *                            play again the audio stream).
319   * @retval 0 if correct communication, else wrong communication
320   */
cs43l22_Stop(uint16_t DeviceAddr,uint32_t CodecPdwnMode)321 uint32_t cs43l22_Stop(uint16_t DeviceAddr, uint32_t CodecPdwnMode)
322 {
323   uint32_t counter = 0;
324 
325   /* Mute the output first */
326   counter += cs43l22_SetMute(DeviceAddr, AUDIO_MUTE_ON);
327 
328   /* Disable the digital soft ramp */
329   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_MISC_CTL, 0x04);
330 
331   /* Power down the DAC and the speaker (PMDAC and PMSPK bits)*/
332   counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL1, 0x9F);
333 
334   Is_cs43l22_Stop = 1;
335   return counter;
336 }
337 
338 /**
339   * @brief Sets higher or lower the codec volume level.
340   * @param DeviceAddr: Device address on communication Bus.
341   * @param Volume: a byte value from 0 to 255 (refer to codec registers
342   *         description for more details).
343   * @retval 0 if correct communication, else wrong communication
344   */
cs43l22_SetVolume(uint16_t DeviceAddr,uint8_t Volume)345 uint32_t cs43l22_SetVolume(uint16_t DeviceAddr, uint8_t Volume)
346 {
347   uint32_t counter = 0;
348   uint8_t convertedvol = VOLUME_CONVERT(Volume);
349 
350   if(Volume > 0xE6)
351   {
352     /* Set the Master volume */
353     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_MASTER_A_VOL, convertedvol - 0xE7);
354     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_MASTER_B_VOL, convertedvol - 0xE7);
355   }
356   else
357   {
358     /* Set the Master volume */
359     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_MASTER_A_VOL, convertedvol + 0x19);
360     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_MASTER_B_VOL, convertedvol + 0x19);
361   }
362 
363   return counter;
364 }
365 
366 /**
367   * @brief Sets new frequency.
368   * @param DeviceAddr: Device address on communication Bus.
369   * @param AudioFreq: Audio frequency used to play the audio stream.
370   * @retval 0 if correct communication, else wrong communication
371   */
cs43l22_SetFrequency(uint16_t DeviceAddr,uint32_t AudioFreq)372 uint32_t cs43l22_SetFrequency(uint16_t DeviceAddr, uint32_t AudioFreq)
373 {
374   return 0;
375 }
376 
377 /**
378   * @brief Enables or disables the mute feature on the audio codec.
379   * @param DeviceAddr: Device address on communication Bus.
380   * @param Cmd: AUDIO_MUTE_ON to enable the mute or AUDIO_MUTE_OFF to disable the
381   *             mute mode.
382   * @retval 0 if correct communication, else wrong communication
383   */
cs43l22_SetMute(uint16_t DeviceAddr,uint32_t Cmd)384 uint32_t cs43l22_SetMute(uint16_t DeviceAddr, uint32_t Cmd)
385 {
386   uint32_t counter = 0;
387 
388   /* Set the Mute mode */
389   if(Cmd == AUDIO_MUTE_ON)
390   {
391     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL2, 0xFF);
392     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_HEADPHONE_A_VOL, 0x01);
393     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_HEADPHONE_B_VOL, 0x01);
394   }
395   else /* AUDIO_MUTE_OFF Disable the Mute */
396   {
397     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_HEADPHONE_A_VOL, 0x00);
398     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_HEADPHONE_B_VOL, 0x00);
399     counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL2, OutputDev);
400   }
401   return counter;
402 }
403 
404 /**
405   * @brief Switch dynamically (while audio file is played) the output target
406   *         (speaker or headphone).
407   * @note This function modifies a global variable of the audio codec driver: OutputDev.
408   * @param DeviceAddr: Device address on communication Bus.
409   * @param Output: specifies the audio output target: OUTPUT_DEVICE_SPEAKER,
410   *         OUTPUT_DEVICE_HEADPHONE, OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO
411   * @retval 0 if correct communication, else wrong communication
412   */
cs43l22_SetOutputMode(uint16_t DeviceAddr,uint8_t Output)413 uint32_t cs43l22_SetOutputMode(uint16_t DeviceAddr, uint8_t Output)
414 {
415   uint32_t counter = 0;
416 
417   switch (Output)
418   {
419     case OUTPUT_DEVICE_SPEAKER:
420       counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL2, 0xFA); /* SPK always ON & HP always OFF */
421       OutputDev = 0xFA;
422       break;
423 
424     case OUTPUT_DEVICE_HEADPHONE:
425       counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL2, 0xAF); /* SPK always OFF & HP always ON */
426       OutputDev = 0xAF;
427       break;
428 
429     case OUTPUT_DEVICE_BOTH:
430       counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL2, 0xAA); /* SPK always ON & HP always ON */
431       OutputDev = 0xAA;
432       break;
433 
434     case OUTPUT_DEVICE_AUTO:
435       counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL2, 0x05); /* Detect the HP or the SPK automatically */
436       OutputDev = 0x05;
437       break;
438 
439     default:
440       counter += CODEC_IO_Write(DeviceAddr, CS43L22_REG_POWER_CTL2, 0x05); /* Detect the HP or the SPK automatically */
441       OutputDev = 0x05;
442       break;
443   }
444   return counter;
445 }
446 
447 /**
448   * @brief Resets cs43l22 registers.
449   * @param DeviceAddr: Device address on communication Bus.
450   * @retval 0 if correct communication, else wrong communication
451   */
cs43l22_Reset(uint16_t DeviceAddr)452 uint32_t cs43l22_Reset(uint16_t DeviceAddr)
453 {
454   return 0;
455 }
456 
457 /**
458   * @brief  Writes/Read a single data.
459   * @param  Addr: I2C address
460   * @param  Reg: Reg address
461   * @param  Value: Data to be written
462   * @retval None
463   */
CODEC_IO_Write(uint8_t Addr,uint8_t Reg,uint8_t Value)464 static uint8_t CODEC_IO_Write(uint8_t Addr, uint8_t Reg, uint8_t Value)
465 {
466   uint32_t result = 0;
467 
468   AUDIO_IO_Write(Addr, Reg, Value);
469 
470 #ifdef VERIFY_WRITTENDATA
471   /* Verify that the data has been correctly written */
472   result = (AUDIO_IO_Read(Addr, Reg) == Value)? 0:1;
473 #endif /* VERIFY_WRITTENDATA */
474 
475   return result;
476 }
477 
478 /**
479   * @}
480   */
481 
482 /**
483   * @}
484   */
485 
486 /**
487   * @}
488   */
489 
490 /**
491   * @}
492   */
493 
494 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
495