| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Apple Onboard Audio driver for Toonie codec |
| * |
| * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> |
| * |
| * This is a driver for the toonie codec chip. This chip is present |
| * on the Mac Mini and is nothing but a DAC. |
| */ |
| #include <linux/delay.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("toonie codec driver for snd-aoa"); |
| |
| #include "../aoa.h" |
| #include "../soundbus/soundbus.h" |
| |
| |
| #define PFX "snd-aoa-codec-toonie: " |
| |
| struct toonie { |
| struct aoa_codec codec; |
| }; |
| #define codec_to_toonie(c) container_of(c, struct toonie, codec) |
| |
| static int toonie_dev_register(struct snd_device *dev) |
| { |
| return 0; |
| } |
| |
| static const struct snd_device_ops ops = { |
| .dev_register = toonie_dev_register, |
| }; |
| |
| static struct transfer_info toonie_transfers[] = { |
| /* This thing *only* has analog output, |
| * the rates are taken from Info.plist |
| * from Darwin. */ |
| { |
| .formats = SNDRV_PCM_FMTBIT_S16_BE | |
| SNDRV_PCM_FMTBIT_S24_BE, |
| .rates = SNDRV_PCM_RATE_32000 | |
| SNDRV_PCM_RATE_44100 | |
| SNDRV_PCM_RATE_48000 | |
| SNDRV_PCM_RATE_88200 | |
| SNDRV_PCM_RATE_96000, |
| }, |
| {} |
| }; |
| |
| static int toonie_usable(struct codec_info_item *cii, |
| struct transfer_info *ti, |
| struct transfer_info *out) |
| { |
| return 1; |
| } |
| |
| #ifdef CONFIG_PM |
| static int toonie_suspend(struct codec_info_item *cii, pm_message_t state) |
| { |
| /* can we turn it off somehow? */ |
| return 0; |
| } |
| |
| static int toonie_resume(struct codec_info_item *cii) |
| { |
| return 0; |
| } |
| #endif /* CONFIG_PM */ |
| |
| static struct codec_info toonie_codec_info = { |
| .transfers = toonie_transfers, |
| .sysclock_factor = 256, |
| .bus_factor = 64, |
| .owner = THIS_MODULE, |
| .usable = toonie_usable, |
| #ifdef CONFIG_PM |
| .suspend = toonie_suspend, |
| .resume = toonie_resume, |
| #endif |
| }; |
| |
| static int toonie_init_codec(struct aoa_codec *codec) |
| { |
| struct toonie *toonie = codec_to_toonie(codec); |
| |
| /* nothing connected? what a joke! */ |
| if (toonie->codec.connected != 1) |
| return -ENOTCONN; |
| |
| if (aoa_snd_device_new(SNDRV_DEV_CODEC, toonie, &ops)) { |
| printk(KERN_ERR PFX "failed to create toonie snd device!\n"); |
| return -ENODEV; |
| } |
| |
| if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev, |
| aoa_get_card(), |
| &toonie_codec_info, toonie)) { |
| printk(KERN_ERR PFX "error creating toonie pcm\n"); |
| snd_device_free(aoa_get_card(), toonie); |
| return -ENODEV; |
| } |
| |
| return 0; |
| } |
| |
| static void toonie_exit_codec(struct aoa_codec *codec) |
| { |
| struct toonie *toonie = codec_to_toonie(codec); |
| |
| if (!toonie->codec.soundbus_dev) { |
| printk(KERN_ERR PFX "toonie_exit_codec called without soundbus_dev!\n"); |
| return; |
| } |
| toonie->codec.soundbus_dev->detach_codec(toonie->codec.soundbus_dev, toonie); |
| } |
| |
| static struct toonie *toonie; |
| |
| static int __init toonie_init(void) |
| { |
| toonie = kzalloc(sizeof(struct toonie), GFP_KERNEL); |
| |
| if (!toonie) |
| return -ENOMEM; |
| |
| strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name)); |
| toonie->codec.owner = THIS_MODULE; |
| toonie->codec.init = toonie_init_codec; |
| toonie->codec.exit = toonie_exit_codec; |
| |
| if (aoa_codec_register(&toonie->codec)) { |
| kfree(toonie); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static void __exit toonie_exit(void) |
| { |
| aoa_codec_unregister(&toonie->codec); |
| kfree(toonie); |
| } |
| |
| module_init(toonie_init); |
| module_exit(toonie_exit); |