Skip to content

Commit e85f013

Browse files
committed
ASoC: tegra: dsp: add support for Fortemedia FM34NE DSP
Signed-off-by: Svyatoslav Ryhel <[email protected]>
1 parent 833535e commit e85f013

File tree

6 files changed

+1221
-0
lines changed

6 files changed

+1221
-0
lines changed

drivers/staging/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,6 @@ source "drivers/staging/vme_user/Kconfig"
8686

8787
source "drivers/staging/p4wifi/Kconfig"
8888

89+
source "drivers/staging/dsp/Kconfig"
90+
8991
endif # STAGING

drivers/staging/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Makefile for staging directory
33

44
obj-y += media/
5+
obj-y += dsp/
56
obj-$(CONFIG_PRISM2_USB) += wlan-ng/
67
obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
78
obj-$(CONFIG_RTL8192U) += rtl8192u/

drivers/staging/dsp/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
config DSP_FM34NE
3+
bool "DSP FM34NE driver"
4+
depends on I2C && GPIOLIB
5+
help
6+
Say Y or M here if you want to add support for Fortemedia FM34NE
7+
DSP mainly used on ASUS T20/T30 Transformers.

drivers/staging/dsp/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
obj-$(CONFIG_DSP_FM34NE) += dsp-fm34ne.o

drivers/staging/dsp/dsp-fm34ne.c

Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Fortemedia FM34NE DSP driver
4+
*/
5+
6+
#include <linux/clk.h>
7+
#include <linux/clk/tegra.h>
8+
#include <linux/delay.h>
9+
#include <linux/gpio/consumer.h>
10+
#include <linux/i2c.h>
11+
#include <linux/module.h>
12+
#include <linux/of.h>
13+
#include <linux/of_gpio.h>
14+
#include <linux/of_device.h>
15+
#include <linux/regulator/consumer.h>
16+
17+
#include "dsp-fm34ne.h"
18+
19+
#define FM34NE_I2C_CHECK 0xC0
20+
#define FM34NE_MAX_RETRY 5
21+
22+
enum state {
23+
FM34NE_BYPASS,
24+
FM34NE_NS_ENABLE,
25+
FM34NE_NS_DISABLE,
26+
FM34NE_MAX,
27+
};
28+
29+
struct fm34ne_dsp_data {
30+
struct i2c_client *client;
31+
32+
struct gpio_desc *bypass_gpio;
33+
struct gpio_desc *reset_gpio;
34+
35+
struct clk *dap_mclk;
36+
struct regulator *vdd_supply;
37+
38+
const struct fm34ne_dsp_devdata *data;
39+
};
40+
41+
static int fm34ne_dsp_write_config(struct i2c_client *client,
42+
const u8* config, size_t size)
43+
{
44+
int ret, i;
45+
46+
for (i = 0; i < FM34NE_MAX_RETRY; i++) {
47+
ret = i2c_master_send(client, config, size);
48+
if (ret > 0)
49+
return 0;
50+
51+
msleep(5);
52+
}
53+
54+
return ret;
55+
}
56+
57+
static int fm34ne_dsp_set_config(struct fm34ne_dsp_data *fm34, int state)
58+
{
59+
struct device *dev = &fm34->client->dev;
60+
61+
const u8 *enable_ns_parameter = fm34->data->enable_noise_suppression;
62+
int enable_ns_length = fm34->data->enable_ns_length;
63+
64+
const u8 *disable_ns_parameter = fm34->data->disable_noise_suppression;
65+
int disable_ns_length = fm34->data->disable_ns_length;
66+
67+
int ret;
68+
69+
gpiod_set_value_cansleep(fm34->bypass_gpio, 1);
70+
msleep(20);
71+
72+
switch (state) {
73+
case FM34NE_NS_ENABLE:
74+
ret = fm34ne_dsp_write_config(fm34->client,
75+
enable_parameter, sizeof(enable_parameter));
76+
if (ret < 0) {
77+
dev_err(dev, "failed to set DSP enable with %d\n", ret);
78+
goto exit;
79+
}
80+
81+
ret = fm34ne_dsp_write_config(fm34->client,
82+
enable_ns_parameter, enable_ns_length);
83+
if (ret < 0) {
84+
dev_err(dev, "failed to enable DSP noise suppression with %d\n", ret);
85+
goto exit;
86+
}
87+
88+
dev_info(dev, "noise suppression enable DSP parameter written\n");
89+
break;
90+
91+
case FM34NE_NS_DISABLE:
92+
ret = fm34ne_dsp_write_config(fm34->client,
93+
enable_parameter, sizeof(enable_parameter));
94+
if (ret < 0) {
95+
dev_err(dev, "failed to set DSP enable with %d\n", ret);
96+
goto exit;
97+
}
98+
99+
ret = fm34ne_dsp_write_config(fm34->client,
100+
disable_ns_parameter, disable_ns_length);
101+
if (ret < 0) {
102+
dev_err(dev, "failed to disable DSP noise suppression with %d\n", ret);
103+
goto exit;
104+
}
105+
106+
dev_info(dev, "noise suppression disable DSP parameter written\n");
107+
break;
108+
109+
case FM34NE_BYPASS:
110+
default:
111+
ret = fm34ne_dsp_write_config(fm34->client,
112+
bypass_parameter, sizeof(bypass_parameter));
113+
if (ret < 0) {
114+
dev_err(dev, "failed to set DSP bypass with %d\n", ret);
115+
goto exit;
116+
}
117+
118+
dev_info(dev, "bypass DSP parameter written\n");
119+
break;
120+
}
121+
122+
exit:
123+
gpiod_set_value_cansleep(fm34->bypass_gpio, 0);
124+
125+
return ret;
126+
}
127+
128+
static int fm34ne_dsp_set_hw(struct fm34ne_dsp_data *fm34)
129+
{
130+
struct device *dev = &fm34->client->dev;
131+
int ret;
132+
133+
ret = clk_prepare_enable(fm34->dap_mclk);
134+
if (ret) {
135+
dev_err(dev, "failed to enable the DSP MCLK: %d\n", ret);
136+
return ret;
137+
}
138+
139+
ret = regulator_enable(fm34->vdd_supply);
140+
if (ret < 0) {
141+
dev_err(dev, "failed to enable vdd power supply\n");
142+
return ret;
143+
}
144+
145+
return 0;
146+
}
147+
148+
static void fm34ne_dsp_reset(struct fm34ne_dsp_data *fm34)
149+
{
150+
gpiod_set_value_cansleep(fm34->reset_gpio, 1);
151+
msleep(10);
152+
153+
gpiod_set_value_cansleep(fm34->reset_gpio, 0);
154+
msleep(100);
155+
}
156+
157+
static int fm34ne_dsp_init_chip(struct fm34ne_dsp_data *fm34)
158+
{
159+
const u8 *input_parameter = fm34->data->input_parameter;
160+
int input_parameter_length = fm34->data->input_parameter_length;
161+
int ret;
162+
163+
ret = fm34ne_dsp_set_hw(fm34);
164+
if (ret)
165+
return ret;
166+
167+
fm34ne_dsp_reset(fm34);
168+
169+
gpiod_set_value_cansleep(fm34->bypass_gpio, 1);
170+
msleep(20);
171+
172+
ret = i2c_smbus_write_byte(fm34->client, FM34NE_I2C_CHECK);
173+
if (ret < 0) {
174+
dev_info(&fm34->client->dev, "initial write failed\n");
175+
msleep(50);
176+
177+
fm34ne_dsp_reset(fm34);
178+
gpiod_set_value_cansleep(fm34->bypass_gpio, 0);
179+
180+
return ret;
181+
}
182+
183+
ret = fm34ne_dsp_write_config(fm34->client,
184+
input_parameter, input_parameter_length);
185+
if (ret < 0)
186+
return -EINVAL;
187+
188+
msleep(100);
189+
gpiod_set_value_cansleep(fm34->bypass_gpio, 0);
190+
191+
dev_info(&fm34->client->dev, "%s detected\n", fm34->data->model);
192+
193+
/* Constantly set DSP to bypass mode for now */
194+
ret = fm34ne_dsp_set_config(fm34, FM34NE_BYPASS);
195+
if (ret)
196+
return ret;
197+
198+
return 0;
199+
}
200+
201+
static int fm34ne_dsp_probe(struct i2c_client *client,
202+
const struct i2c_device_id *id)
203+
{
204+
struct device *dev = &client->dev;
205+
struct fm34ne_dsp_data *fm34;
206+
int ret;
207+
208+
fm34 = devm_kzalloc(dev, sizeof(*fm34), GFP_KERNEL);
209+
if (!fm34)
210+
return -ENOMEM;
211+
212+
i2c_set_clientdata(client, fm34);
213+
fm34->client = client;
214+
215+
fm34->dap_mclk = devm_clk_get_optional(dev, "mclk");
216+
if (IS_ERR(fm34->dap_mclk))
217+
return dev_err_probe(dev, PTR_ERR(fm34->dap_mclk),
218+
"can't retrieve DSP MCLK\n");
219+
220+
fm34->vdd_supply = devm_regulator_get(dev, "vdd");
221+
if (IS_ERR(fm34->vdd_supply))
222+
return dev_err_probe(dev, PTR_ERR(fm34->vdd_supply),
223+
"failed to get vdd regulator\n");
224+
225+
fm34->reset_gpio = devm_gpiod_get_optional(dev, "reset",
226+
GPIOD_OUT_LOW);
227+
if (IS_ERR(fm34->reset_gpio))
228+
return dev_err_probe(dev, PTR_ERR(fm34->reset_gpio),
229+
"failed to get reset GPIO\n");
230+
231+
/*
232+
* Bypass gpio is used to set audio into bypass mode
233+
* in relation to dsp to be able to program it. Once
234+
* programming is done, bypass gpio has to be set to
235+
* low to return dsp into audio processing.
236+
*/
237+
fm34->bypass_gpio = devm_gpiod_get_optional(dev, "bypass",
238+
GPIOD_OUT_LOW);
239+
if (IS_ERR(fm34->bypass_gpio))
240+
return dev_err_probe(dev, PTR_ERR(fm34->bypass_gpio),
241+
"failed to get bypass GPIO\n");
242+
243+
fm34->data = of_device_get_match_data(dev);
244+
if (!fm34->data)
245+
return -ENODEV;
246+
247+
ret = fm34ne_dsp_init_chip(fm34);
248+
if (ret)
249+
return dev_err_probe(dev, ret,
250+
"failed to init DSP chip\n");
251+
252+
return 0;
253+
}
254+
255+
static int fm34ne_dsp_suspend(struct device *dev)
256+
{
257+
struct i2c_client *client = to_i2c_client(dev);
258+
struct fm34ne_dsp_data *fm34 = i2c_get_clientdata(client);
259+
260+
gpiod_set_value_cansleep(fm34->bypass_gpio, 0);
261+
262+
regulator_disable(fm34->vdd_supply);
263+
264+
clk_disable_unprepare(fm34->dap_mclk);
265+
266+
return 0;
267+
}
268+
269+
static int fm34ne_dsp_resume(struct device *dev)
270+
{
271+
struct i2c_client *client = to_i2c_client(dev);
272+
struct fm34ne_dsp_data *fm34 = i2c_get_clientdata(client);
273+
int ret;
274+
275+
ret = fm34ne_dsp_init_chip(fm34);
276+
if (ret)
277+
dev_err(&client->dev, "failed to re-init DSP chip with %d\n", ret);
278+
279+
return 0;
280+
}
281+
282+
static DEFINE_SIMPLE_DEV_PM_OPS(fm34ne_dsp_pm_ops,
283+
fm34ne_dsp_suspend, fm34ne_dsp_resume);
284+
285+
static const struct fm34ne_dsp_devdata tf101_dsp_data = {
286+
.model = "ASUS Eee Pad Trnasformer TF101",
287+
.input_parameter = TF101_input_parameter,
288+
.input_parameter_length = sizeof(TF101_input_parameter),
289+
.enable_noise_suppression = TF101_enable_NS,
290+
.enable_ns_length = sizeof(TF101_enable_NS),
291+
.disable_noise_suppression = TF101_disable_NS,
292+
.disable_ns_length = sizeof(TF101_disable_NS),
293+
};
294+
295+
static const struct fm34ne_dsp_devdata tf201_dsp_data = {
296+
.model = "ASUS Transformer Prime TF201",
297+
.input_parameter = TF201_input_parameter,
298+
.input_parameter_length = sizeof(TF201_input_parameter),
299+
.enable_noise_suppression = TF201_enable_NS,
300+
.enable_ns_length = sizeof(TF201_enable_NS),
301+
.disable_noise_suppression = TF201_disable_NS,
302+
.disable_ns_length = sizeof(TF201_disable_NS),
303+
};
304+
305+
static const struct fm34ne_dsp_devdata tf300t_dsp_data = {
306+
.model = "ASUS Transformer PAD TF300T",
307+
.input_parameter = TF300T_input_parameter,
308+
.input_parameter_length = sizeof(TF300T_input_parameter),
309+
.enable_noise_suppression = TF201_enable_NS,
310+
.enable_ns_length = sizeof(TF201_enable_NS),
311+
.disable_noise_suppression = TF201_disable_NS,
312+
.disable_ns_length = sizeof(TF201_disable_NS),
313+
};
314+
315+
static const struct fm34ne_dsp_devdata tf700t_dsp_data = {
316+
.model = "ASUS Transformer Infinity TF700T",
317+
.input_parameter = TF700T_input_parameter,
318+
.input_parameter_length = sizeof(TF700T_input_parameter),
319+
.enable_noise_suppression = TF700T_enable_NS,
320+
.enable_ns_length = sizeof(TF700T_enable_NS),
321+
.disable_noise_suppression = TF700T_disable_NS,
322+
.disable_ns_length = sizeof(TF700T_disable_NS),
323+
};
324+
325+
static const struct fm34ne_dsp_devdata chagall_dsp_data = {
326+
.model = "Pegatron Chagall",
327+
.input_parameter = TF300T_input_parameter,
328+
.input_parameter_length = sizeof(TF300T_input_parameter),
329+
.enable_noise_suppression = TF201_enable_NS,
330+
.enable_ns_length = sizeof(TF201_enable_NS),
331+
.disable_noise_suppression = TF201_disable_NS,
332+
.disable_ns_length = sizeof(TF201_disable_NS),
333+
};
334+
335+
static const struct of_device_id fm34ne_dsp_match[] = {
336+
{ .compatible = "asus,tf101-dsp", .data = &tf101_dsp_data },
337+
{ .compatible = "asus,tf201-dsp", .data = &tf201_dsp_data },
338+
{ .compatible = "asus,tf300t-dsp", .data = &tf300t_dsp_data },
339+
{ .compatible = "asus,tf700t-dsp", .data = &tf700t_dsp_data },
340+
{ .compatible = "pegatron,chagall-dsp", .data = &chagall_dsp_data },
341+
{ }
342+
};
343+
MODULE_DEVICE_TABLE(of, fm34ne_dsp_match);
344+
345+
static const struct i2c_device_id fm34ne_dsp_id[] = {
346+
{ "dsp_fm34ne", 0 },
347+
{ }
348+
};
349+
MODULE_DEVICE_TABLE(i2c, fm34ne_dsp_id);
350+
351+
static struct i2c_driver fm34ne_dsp_driver = {
352+
.driver = {
353+
.name = "fm34ne-dsp",
354+
.pm = pm_sleep_ptr(&fm34ne_dsp_pm_ops),
355+
.of_match_table = fm34ne_dsp_match,
356+
},
357+
.probe = fm34ne_dsp_probe,
358+
.id_table = fm34ne_dsp_id,
359+
};
360+
module_i2c_driver(fm34ne_dsp_driver);
361+
362+
MODULE_AUTHOR("Svyatoslav Ryhel <[email protected]>");
363+
MODULE_DESCRIPTION("Fortemedia FM34NE DSP driver");
364+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)