18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Apple Onboard Audio driver for Toonie codec 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This is a driver for the toonie codec chip. This chip is present 88c2ecf20Sopenharmony_ci * on the Mac Mini and is nothing but a DAC. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); 148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("toonie codec driver for snd-aoa"); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "../aoa.h" 188c2ecf20Sopenharmony_ci#include "../soundbus/soundbus.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define PFX "snd-aoa-codec-toonie: " 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct toonie { 248c2ecf20Sopenharmony_ci struct aoa_codec codec; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci#define codec_to_toonie(c) container_of(c, struct toonie, codec) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int toonie_dev_register(struct snd_device *dev) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return 0; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct snd_device_ops ops = { 348c2ecf20Sopenharmony_ci .dev_register = toonie_dev_register, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic struct transfer_info toonie_transfers[] = { 388c2ecf20Sopenharmony_ci /* This thing *only* has analog output, 398c2ecf20Sopenharmony_ci * the rates are taken from Info.plist 408c2ecf20Sopenharmony_ci * from Darwin. */ 418c2ecf20Sopenharmony_ci { 428c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_BE | 438c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE, 448c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_32000 | 458c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_44100 | 468c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000 | 478c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_88200 | 488c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000, 498c2ecf20Sopenharmony_ci }, 508c2ecf20Sopenharmony_ci {} 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int toonie_usable(struct codec_info_item *cii, 548c2ecf20Sopenharmony_ci struct transfer_info *ti, 558c2ecf20Sopenharmony_ci struct transfer_info *out) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci return 1; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 618c2ecf20Sopenharmony_cistatic int toonie_suspend(struct codec_info_item *cii, pm_message_t state) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci /* can we turn it off somehow? */ 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int toonie_resume(struct codec_info_item *cii) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct codec_info toonie_codec_info = { 748c2ecf20Sopenharmony_ci .transfers = toonie_transfers, 758c2ecf20Sopenharmony_ci .sysclock_factor = 256, 768c2ecf20Sopenharmony_ci .bus_factor = 64, 778c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 788c2ecf20Sopenharmony_ci .usable = toonie_usable, 798c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 808c2ecf20Sopenharmony_ci .suspend = toonie_suspend, 818c2ecf20Sopenharmony_ci .resume = toonie_resume, 828c2ecf20Sopenharmony_ci#endif 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int toonie_init_codec(struct aoa_codec *codec) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct toonie *toonie = codec_to_toonie(codec); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* nothing connected? what a joke! */ 908c2ecf20Sopenharmony_ci if (toonie->codec.connected != 1) 918c2ecf20Sopenharmony_ci return -ENOTCONN; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (aoa_snd_device_new(SNDRV_DEV_CODEC, toonie, &ops)) { 948c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "failed to create toonie snd device!\n"); 958c2ecf20Sopenharmony_ci return -ENODEV; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev, 998c2ecf20Sopenharmony_ci aoa_get_card(), 1008c2ecf20Sopenharmony_ci &toonie_codec_info, toonie)) { 1018c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "error creating toonie pcm\n"); 1028c2ecf20Sopenharmony_ci snd_device_free(aoa_get_card(), toonie); 1038c2ecf20Sopenharmony_ci return -ENODEV; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void toonie_exit_codec(struct aoa_codec *codec) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct toonie *toonie = codec_to_toonie(codec); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (!toonie->codec.soundbus_dev) { 1148c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "toonie_exit_codec called without soundbus_dev!\n"); 1158c2ecf20Sopenharmony_ci return; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci toonie->codec.soundbus_dev->detach_codec(toonie->codec.soundbus_dev, toonie); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct toonie *toonie; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int __init toonie_init(void) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci toonie = kzalloc(sizeof(struct toonie), GFP_KERNEL); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!toonie) 1278c2ecf20Sopenharmony_ci return -ENOMEM; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name)); 1308c2ecf20Sopenharmony_ci toonie->codec.owner = THIS_MODULE; 1318c2ecf20Sopenharmony_ci toonie->codec.init = toonie_init_codec; 1328c2ecf20Sopenharmony_ci toonie->codec.exit = toonie_exit_codec; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (aoa_codec_register(&toonie->codec)) { 1358c2ecf20Sopenharmony_ci kfree(toonie); 1368c2ecf20Sopenharmony_ci return -EINVAL; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void __exit toonie_exit(void) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci aoa_codec_unregister(&toonie->codec); 1458c2ecf20Sopenharmony_ci kfree(toonie); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cimodule_init(toonie_init); 1498c2ecf20Sopenharmony_cimodule_exit(toonie_exit); 150