diff options
Diffstat (limited to 'drivers/media/video/stk1160/stk1160-ac97.c')
-rw-r--r-- | drivers/media/video/stk1160/stk1160-ac97.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/drivers/media/video/stk1160/stk1160-ac97.c b/drivers/media/video/stk1160/stk1160-ac97.c new file mode 100644 index 00000000000..8d325f50c87 --- /dev/null +++ b/drivers/media/video/stk1160/stk1160-ac97.c | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * STK1160 driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Ezequiel Garcia | ||
5 | * <elezegarcia--a.t--gmail.com> | ||
6 | * | ||
7 | * Based on Easycap driver by R.M. Thomas | ||
8 | * Copyright (C) 2010 R.M. Thomas | ||
9 | * <rmthomas--a.t--sciolus.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/initval.h> | ||
26 | #include <sound/ac97_codec.h> | ||
27 | |||
28 | #include "stk1160.h" | ||
29 | #include "stk1160-reg.h" | ||
30 | |||
31 | static struct snd_ac97 *stk1160_ac97; | ||
32 | |||
33 | static void stk1160_write_ac97(struct snd_ac97 *ac97, u16 reg, u16 value) | ||
34 | { | ||
35 | struct stk1160 *dev = ac97->private_data; | ||
36 | |||
37 | /* Set codec register address */ | ||
38 | stk1160_write_reg(dev, STK1160_AC97_ADDR, reg); | ||
39 | |||
40 | /* Set codec command */ | ||
41 | stk1160_write_reg(dev, STK1160_AC97_CMD, value & 0xff); | ||
42 | stk1160_write_reg(dev, STK1160_AC97_CMD + 1, (value & 0xff00) >> 8); | ||
43 | |||
44 | /* | ||
45 | * Set command write bit to initiate write operation. | ||
46 | * The bit will be cleared when transfer is done. | ||
47 | */ | ||
48 | stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8c); | ||
49 | } | ||
50 | |||
51 | static u16 stk1160_read_ac97(struct snd_ac97 *ac97, u16 reg) | ||
52 | { | ||
53 | struct stk1160 *dev = ac97->private_data; | ||
54 | u8 vall = 0; | ||
55 | u8 valh = 0; | ||
56 | |||
57 | /* Set codec register address */ | ||
58 | stk1160_write_reg(dev, STK1160_AC97_ADDR, reg); | ||
59 | |||
60 | /* | ||
61 | * Set command read bit to initiate read operation. | ||
62 | * The bit will be cleared when transfer is done. | ||
63 | */ | ||
64 | stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8b); | ||
65 | |||
66 | /* Retrieve register value */ | ||
67 | stk1160_read_reg(dev, STK1160_AC97_CMD, &vall); | ||
68 | stk1160_read_reg(dev, STK1160_AC97_CMD + 1, &valh); | ||
69 | |||
70 | return (valh << 8) | vall; | ||
71 | } | ||
72 | |||
73 | static void stk1160_reset_ac97(struct snd_ac97 *ac97) | ||
74 | { | ||
75 | struct stk1160 *dev = ac97->private_data; | ||
76 | /* Two-step reset AC97 interface and hardware codec */ | ||
77 | stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x94); | ||
78 | stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x88); | ||
79 | |||
80 | /* Set 16-bit audio data and choose L&R channel*/ | ||
81 | stk1160_write_reg(dev, STK1160_AC97CTL_1 + 2, 0x01); | ||
82 | } | ||
83 | |||
84 | static struct snd_ac97_bus_ops stk1160_ac97_ops = { | ||
85 | .read = stk1160_read_ac97, | ||
86 | .write = stk1160_write_ac97, | ||
87 | .reset = stk1160_reset_ac97, | ||
88 | }; | ||
89 | |||
90 | int stk1160_ac97_register(struct stk1160 *dev) | ||
91 | { | ||
92 | struct snd_card *card = NULL; | ||
93 | struct snd_ac97_bus *ac97_bus; | ||
94 | struct snd_ac97_template ac97_template; | ||
95 | int rc; | ||
96 | |||
97 | /* | ||
98 | * Just want a card to access ac96 controls, | ||
99 | * the actual capture interface will be handled by snd-usb-audio | ||
100 | */ | ||
101 | rc = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, | ||
102 | THIS_MODULE, 0, &card); | ||
103 | if (rc < 0) | ||
104 | return rc; | ||
105 | |||
106 | snd_card_set_dev(card, dev->dev); | ||
107 | |||
108 | /* TODO: I'm not sure where should I get these names :-( */ | ||
109 | snprintf(card->shortname, sizeof(card->shortname), | ||
110 | "stk1160-mixer"); | ||
111 | snprintf(card->longname, sizeof(card->longname), | ||
112 | "stk1160 ac97 codec mixer control"); | ||
113 | strncpy(card->driver, dev->dev->driver->name, sizeof(card->driver)); | ||
114 | |||
115 | rc = snd_ac97_bus(card, 0, &stk1160_ac97_ops, NULL, &ac97_bus); | ||
116 | if (rc) | ||
117 | goto err; | ||
118 | |||
119 | /* We must set private_data before calling snd_ac97_mixer */ | ||
120 | memset(&ac97_template, 0, sizeof(ac97_template)); | ||
121 | ac97_template.private_data = dev; | ||
122 | ac97_template.scaps = AC97_SCAP_SKIP_MODEM; | ||
123 | rc = snd_ac97_mixer(ac97_bus, &ac97_template, &stk1160_ac97); | ||
124 | if (rc) | ||
125 | goto err; | ||
126 | |||
127 | dev->snd_card = card; | ||
128 | rc = snd_card_register(card); | ||
129 | if (rc) | ||
130 | goto err; | ||
131 | |||
132 | return 0; | ||
133 | |||
134 | err: | ||
135 | dev->snd_card = NULL; | ||
136 | if (card) | ||
137 | snd_card_free(card); | ||
138 | return rc; | ||
139 | } | ||
140 | |||
141 | int stk1160_ac97_unregister(struct stk1160 *dev) | ||
142 | { | ||
143 | struct snd_card *card = dev->snd_card; | ||
144 | |||
145 | /* | ||
146 | * We need to check usb_device, | ||
147 | * because ac97 release attempts to communicate with codec | ||
148 | */ | ||
149 | if (card && dev->udev) | ||
150 | snd_card_free(card); | ||
151 | |||
152 | return 0; | ||
153 | } | ||