Fix initialization. Use macro for sampling rate.
[bertos.git] / bertos / drv / wm8731.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2011 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \author Daniele Basile <asterix@develer.com>
34  *
35  * \brief WM8731 Audio codec 2 wire driver.
36  *
37  */
38
39 #include "wm8731.h"
40
41 #include "hw/hw_wm8731.h"
42 #include "cfg/cfg_wm8731.h"
43
44 // Define logging setting (for cfg/log.h module).
45 #define LOG_LEVEL   WM8731_LOG_LEVEL
46 #define LOG_FORMAT  WM8731_LOG_FORMAT
47 #include <cfg/log.h>
48 #include <cfg/macros.h>
49
50 #include <cpu/irq.h>
51
52 #include <drv/i2c.h>
53
54
55 static void wm8731_write(Wm8731 *ctx, uint8_t reg, uint16_t value)
56 {
57
58         uint16_t tmp = ((reg & 0x7F) << 9) | (value & 0x1FF);
59
60         i2c_start_w(ctx->i2c, ctx->addr, 2, I2C_STOP);
61         i2c_putc(ctx->i2c, (uint8_t)((tmp & 0xFF00) >> 8));
62         i2c_putc(ctx->i2c, (uint8_t)(tmp & 0xFF));
63
64         int err = i2c_error(ctx->i2c);
65         if (err)
66                 LOG_ERR("Error[%d] while send command to codec.\n", err);
67
68 }
69
70 #define RANGECONV(data, y1, y2) (((((int32_t)(data)) * ((y2) - (y1))) / ((1 << 8) - 1)) + (y1))
71 void wm8731_setVolume(Wm8731 *ctx, uint16_t device, uint8_t volume)
72 {
73         uint16_t value;
74
75         if (device & WM8731_LINE_IN)
76         {
77                 if (!volume)
78                 {
79                         wm8731_write(ctx, WM8731_REG_RIGHT_LINEIN, BV(WM8731_LINMUTE_BIT) | BV(WM8731_RLINBOTH_BIT));
80                         wm8731_write(ctx, WM8731_REG_LEFT_LINEIN, BV(WM8731_LINMUTE_BIT) | BV(WM8731_LRINBOTH_BIT));
81                 }
82
83                 value = DIV_ROUND(volume * WM8731_LINVOL_BITS_MASK, 100);
84
85                 wm8731_write(ctx, WM8731_REG_RIGHT_LINEIN, ~BV(WM8731_LINMUTE_BIT) | value);
86                 wm8731_write(ctx, WM8731_REG_LEFT_LINEIN, ~BV(WM8731_RINMUTE_BIT) | value);
87                 LOG_INFO("Set LINE IN vol[%d]%% raw[%d]\n", volume, value);
88         }
89
90         if (device & WM8731_HEADPHONE)
91         {
92                 value = DIV_ROUND(volume * WM8731_RHPVOL_BITS_MASK, 100);
93
94                 wm8731_write(ctx, WM8731_REG_RIGHT_HPOUT, value | BV(WM8731_RZCEN_BIT) | BV(WM8731_RLHPBOTH_BIT));
95                 wm8731_write(ctx, WM8731_REG_LEFT_HPOUT, value  | BV(WM8731_LZCEN_BIT) | BV(WM8731_LRHPBOTH_BIT));
96                 LOG_INFO("Set HEADPHONE vol[%d]%% raw[%d]\n", volume, value);
97         }
98
99 }
100
101 void wm8731_powerOn(Wm8731 *ctx)
102 {
103         LOG_INFO("Power on codec\n");
104         wm8731_write(ctx, WM8731_REG_PWDOWN_CTRL, ~BV(WM8731_POWEROFF_BIT) & 0x7F);
105 }
106
107 void wm8731_powerOff(Wm8731 *ctx)
108 {
109         LOG_INFO("Power off codec\n");
110         wm8731_write(ctx, WM8731_REG_PWDOWN_CTRL, BV(WM8731_POWEROFF_BIT) & 0x7F);
111 }
112
113 void wm8731_powerOnDevices(Wm8731 *ctx, uint16_t device)
114 {
115         wm8731_write(ctx, WM8731_REG_PWDOWN_CTRL, ~device & 0x7F);
116         LOG_INFO("Turn on the devices[%x]\n", ~device & 0x7F);
117 }
118
119 void wm8731_powerOffDevices(Wm8731 *ctx, uint16_t device)
120 {
121         wm8731_write(ctx, WM8731_REG_PWDOWN_CTRL, device & 0x7F);
122         LOG_INFO("Turn off the devices[%x]\n", device);
123 }
124
125 void wm8731_init(Wm8731 *ctx, I2c *i2c, uint8_t codec_addr)
126 {
127         ctx->i2c = i2c;
128         ctx->addr = codec_addr;
129
130         WM8731_PIN_INIT();
131         WM8731_MCLK_INIT();
132
133         LOG_INFO("Init WM8731 codec.\n");
134
135         /* Reset codec and active it */
136         wm8731_write(ctx, WM8731_REG_RESET, 0);
137
138
139         /* Configure the codec */
140         wm8731_write(ctx, WM8731_REG_DIGITAL_PATH_CTRL, CONFIG_WM8731_DEEMP | CONFIG_WM8731_DAPC | (CONFIG_WM8731_DACMU << WM8731_DACMU));
141
142         #if CONFIG_WM8731_MICBOOST
143         wm8731_write(ctx, WM8731_REG_ANALOGUE_PATH_CTRL, BV(WM8731_MICBOOST) | CONFIG_WM8731_INSEL | CONFIG_WM8731_BYPASS | CONFIG_WM8731_SIDEATT);
144         #else
145         wm8731_write(ctx, WM8731_REG_ANALOGUE_PATH_CTRL, (CONFIG_WM8731_INSEL | CONFIG_WM8731_BYPASS | CONFIG_WM8731_SIDEATT) & ~BV(WM8731_MICBOOST));
146         #endif
147
148         #if CONFIG_WM8731_MS
149         wm8731_write(ctx, WM8731_REG_DA_INTERFACE_FORMAT, CONFIG_WM8731_INTERFACE_FORMAT | CONFIG_WM8731_IWL_BITS | BV(WM8731_MS_BIT));
150         #else
151         wm8731_write(ctx, WM8731_REG_DA_INTERFACE_FORMAT,
152                 (CONFIG_WM8731_INTERFACE_FORMAT | CONFIG_WM8731_IWL_BITS | BV(WM8731_MS_BIT)) & ~BV(WM8731_MS_BIT));
153         #endif
154
155         wm8731_write(ctx, WM8731_REG_SAMPLECTRL, CONFIG_WM8731_SAMPLING_RATES);
156
157         /* By default we turn on all devices and disable only the outclock */
158         wm8731_write(ctx, WM8731_REG_ACTIVE_CTRL, 1);
159         wm8731_write(ctx, WM8731_REG_PWDOWN_CTRL, 0x40);
160 }