|
6 | 6 | * Cirrus Logic International Semiconductor Ltd. |
7 | 7 | */ |
8 | 8 |
|
| 9 | +#include <linux/acpi.h> |
| 10 | +#include <linux/cleanup.h> |
| 11 | +#include <linux/i2c.h> |
9 | 12 | #include <linux/init.h> |
10 | 13 | #include <linux/slab.h> |
11 | 14 | #include <linux/module.h> |
| 15 | +#include <linux/spi/spi.h> |
12 | 16 | #include <sound/core.h> |
13 | 17 | #include <linux/mutex.h> |
14 | 18 | #include <linux/iopoll.h> |
15 | 19 |
|
16 | 20 | #include "cs8409.h" |
| 21 | +#include "../side-codecs/hda_component.h" |
17 | 22 |
|
18 | 23 | /****************************************************************************** |
19 | 24 | * CS8409 Specific Functions |
@@ -1216,6 +1221,172 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, |
1216 | 1221 | } |
1217 | 1222 | } |
1218 | 1223 |
|
| 1224 | +static int cs8409_comp_bind(struct device *dev) |
| 1225 | +{ |
| 1226 | + struct hda_codec *codec = dev_to_hda_codec(dev); |
| 1227 | + struct cs8409_spec *spec = codec->spec; |
| 1228 | + |
| 1229 | + return hda_component_manager_bind(codec, &spec->comps); |
| 1230 | +} |
| 1231 | + |
| 1232 | +static void cs8409_comp_unbind(struct device *dev) |
| 1233 | +{ |
| 1234 | + struct hda_codec *codec = dev_to_hda_codec(dev); |
| 1235 | + struct cs8409_spec *spec = codec->spec; |
| 1236 | + |
| 1237 | + hda_component_manager_unbind(codec, &spec->comps); |
| 1238 | +} |
| 1239 | + |
| 1240 | +static const struct component_master_ops cs8409_comp_master_ops = { |
| 1241 | + .bind = cs8409_comp_bind, |
| 1242 | + .unbind = cs8409_comp_unbind, |
| 1243 | +}; |
| 1244 | + |
| 1245 | +static void cs8409_comp_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, |
| 1246 | + struct snd_pcm_substream *sub, int action) |
| 1247 | +{ |
| 1248 | + struct cs8409_spec *spec = codec->spec; |
| 1249 | + |
| 1250 | + hda_component_manager_playback_hook(&spec->comps, action); |
| 1251 | +} |
| 1252 | + |
| 1253 | +static void cs8409_cdb35l56_four_hw_init(struct hda_codec *codec) |
| 1254 | +{ |
| 1255 | + const struct cs8409_cir_param *seq = cs8409_cdb35l56_four_hw_cfg; |
| 1256 | + |
| 1257 | + for (; seq->nid; seq++) |
| 1258 | + cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); |
| 1259 | +} |
| 1260 | + |
| 1261 | +static int cs8409_spk_sw_get(struct snd_kcontrol *kcontrol, |
| 1262 | + struct snd_ctl_elem_value *ucontrol) |
| 1263 | +{ |
| 1264 | + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
| 1265 | + struct cs8409_spec *spec = codec->spec; |
| 1266 | + |
| 1267 | + ucontrol->value.integer.value[0] = !spec->speaker_muted; |
| 1268 | + |
| 1269 | + return 0; |
| 1270 | +} |
| 1271 | + |
| 1272 | +static int cs8409_spk_sw_put(struct snd_kcontrol *kcontrol, |
| 1273 | + struct snd_ctl_elem_value *ucontrol) |
| 1274 | +{ |
| 1275 | + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
| 1276 | + struct cs8409_spec *spec = codec->spec; |
| 1277 | + bool muted = !ucontrol->value.integer.value[0]; |
| 1278 | + |
| 1279 | + if (muted == spec->speaker_muted) |
| 1280 | + return 0; |
| 1281 | + |
| 1282 | + spec->speaker_muted = muted; |
| 1283 | + |
| 1284 | + return 1; |
| 1285 | +} |
| 1286 | + |
| 1287 | +static const struct snd_kcontrol_new cs8409_spk_sw_component_ctrl = { |
| 1288 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
| 1289 | + .info = snd_ctl_boolean_mono_info, |
| 1290 | + .get = cs8409_spk_sw_get, |
| 1291 | + .put = cs8409_spk_sw_put, |
| 1292 | +}; |
| 1293 | + |
| 1294 | +void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec, |
| 1295 | + const struct hda_fixup *fix, |
| 1296 | + int action) |
| 1297 | +{ |
| 1298 | + struct device *dev = hda_codec_dev(codec); |
| 1299 | + struct cs8409_spec *spec = codec->spec; |
| 1300 | + struct acpi_device *adev; |
| 1301 | + const char *bus = NULL; |
| 1302 | + static const struct { |
| 1303 | + const char *hid; |
| 1304 | + const char *name; |
| 1305 | + } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" }, |
| 1306 | + { "CSC3556", "cs35l56-hda" }, |
| 1307 | + { "CSC3557", "cs35l57-hda" }}; |
| 1308 | + char *match; |
| 1309 | + int i, count = 0, count_devindex = 0; |
| 1310 | + int ret; |
| 1311 | + |
| 1312 | + switch (action) { |
| 1313 | + case HDA_FIXUP_ACT_PRE_PROBE: { |
| 1314 | + for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) { |
| 1315 | + adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1); |
| 1316 | + if (adev) |
| 1317 | + break; |
| 1318 | + } |
| 1319 | + if (!adev) { |
| 1320 | + dev_err(dev, "Failed to find ACPI entry for a Cirrus Amp\n"); |
| 1321 | + return; |
| 1322 | + } |
| 1323 | + |
| 1324 | + count = i2c_acpi_client_count(adev); |
| 1325 | + if (count > 0) { |
| 1326 | + bus = "i2c"; |
| 1327 | + } else { |
| 1328 | + count = acpi_spi_count_resources(adev); |
| 1329 | + if (count > 0) |
| 1330 | + bus = "spi"; |
| 1331 | + } |
| 1332 | + |
| 1333 | + struct fwnode_handle *fwnode __free(fwnode_handle) = |
| 1334 | + fwnode_handle_get(acpi_fwnode_handle(adev)); |
| 1335 | + acpi_dev_put(adev); |
| 1336 | + |
| 1337 | + if (!bus) { |
| 1338 | + dev_err(dev, "Did not find any buses for %s\n", acpi_ids[i].hid); |
| 1339 | + return; |
| 1340 | + } |
| 1341 | + |
| 1342 | + if (!fwnode) { |
| 1343 | + dev_err(dev, "Could not get fwnode for %s\n", acpi_ids[i].hid); |
| 1344 | + return; |
| 1345 | + } |
| 1346 | + |
| 1347 | + /* |
| 1348 | + * When available the cirrus,dev-index property is an accurate |
| 1349 | + * count of the amps in a system and is used in preference to |
| 1350 | + * the count of bus devices that can contain additional address |
| 1351 | + * alias entries. |
| 1352 | + */ |
| 1353 | + count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index"); |
| 1354 | + if (count_devindex > 0) |
| 1355 | + count = count_devindex; |
| 1356 | + |
| 1357 | + match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name); |
| 1358 | + if (!match) |
| 1359 | + return; |
| 1360 | + dev_info(dev, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match); |
| 1361 | + |
| 1362 | + ret = hda_component_manager_init(codec, &spec->comps, count, bus, |
| 1363 | + acpi_ids[i].hid, match, |
| 1364 | + &cs8409_comp_master_ops); |
| 1365 | + if (ret) |
| 1366 | + return; |
| 1367 | + |
| 1368 | + spec->gen.pcm_playback_hook = cs8409_comp_playback_hook; |
| 1369 | + |
| 1370 | + snd_hda_add_verbs(codec, cs8409_cdb35l56_four_init_verbs); |
| 1371 | + snd_hda_sequence_write(codec, cs8409_cdb35l56_four_init_verbs); |
| 1372 | + break; |
| 1373 | + } |
| 1374 | + case HDA_FIXUP_ACT_PROBE: |
| 1375 | + spec->speaker_muted = 0; /* speakers begin enabled */ |
| 1376 | + snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch", |
| 1377 | + &cs8409_spk_sw_component_ctrl); |
| 1378 | + spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; |
| 1379 | + snd_hda_codec_set_name(codec, "CS8409/CS35L56"); |
| 1380 | + break; |
| 1381 | + case HDA_FIXUP_ACT_INIT: |
| 1382 | + cs8409_cdb35l56_four_hw_init(codec); |
| 1383 | + break; |
| 1384 | + case HDA_FIXUP_ACT_FREE: |
| 1385 | + hda_component_manager_free(&spec->comps, &cs8409_comp_master_ops); |
| 1386 | + break; |
| 1387 | + } |
| 1388 | +} |
| 1389 | + |
1219 | 1390 | /****************************************************************************** |
1220 | 1391 | * Dolphin Specific Functions |
1221 | 1392 | * CS8409/ 2 X CS42L42 |
@@ -1473,3 +1644,4 @@ module_hda_codec_driver(cs8409_driver); |
1473 | 1644 |
|
1474 | 1645 | MODULE_LICENSE("GPL"); |
1475 | 1646 | MODULE_DESCRIPTION("Cirrus Logic HDA bridge"); |
| 1647 | +MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); |
0 commit comments