/* * Extended support for CS35L41 Amp * * Copyright 2017 Cirrus Logic * * Author: David Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #define CIRRUS_AMP_CLASS_NAME "cirrus" struct class *cirrus_amp_class; static int cirrus_amp_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct device_node *amp_node; const char **mfd_suffixes; const char **bd_suffixes; int ret, num_amps, i, j; if (np) { ret = of_property_read_u32(np, "cirrus,num-amps", &num_amps); if (ret < 0) { dev_err(&pdev->dev, "cirrus_amp: failed to parse num-amps\n"); return -EINVAL; } mfd_suffixes = kzalloc(num_amps * sizeof(char *), GFP_KERNEL); if (mfd_suffixes == NULL) { dev_err(&pdev->dev, "Failed to allocate\n"); return -ENOMEM; } bd_suffixes = kzalloc(num_amps * sizeof(char *), GFP_KERNEL); if (bd_suffixes == NULL) { dev_err(&pdev->dev, "Failed to allocate\n"); return -ENOMEM; } for (i = 0; i < num_amps; i++) { amp_node = of_parse_phandle(np, "cirrus,amps", i); dev_dbg(&pdev->dev, "Found linked amp: %s\n", amp_node->full_name); ret = of_property_read_string(amp_node, "cirrus,mfd-suffix", &mfd_suffixes[i]); if (ret < 0) dev_err(&pdev->dev, "No MFD suffix found for amp: %s\n", amp_node->full_name); ret = of_property_read_string(amp_node, "cirrus,bd-suffix", &bd_suffixes[i]); if (ret < 0) dev_dbg(&pdev->dev, "No BD suffix found for amp: %s\n", amp_node->full_name); of_node_put(amp_node); } for (i = 0; i < num_amps; i++) { for (j = 0; j < num_amps; j++) { if (mfd_suffixes[i] == NULL || mfd_suffixes[j] == NULL) { dev_err(&pdev->dev, "MFD suffixes incomplete\n"); kfree(mfd_suffixes); return -EINVAL; } if (strcmp(mfd_suffixes[i], mfd_suffixes[j]) == 0 && i != j) { dev_err(&pdev->dev, "MFD suffixes must be unique\n"); dev_err(&pdev->dev, "Found duplicate suffix: %s\n", mfd_suffixes[i]); kfree(mfd_suffixes); return -EINVAL; } /*bd_suffixes can be empty but must be unique*/ if (bd_suffixes[i] && bd_suffixes[j]) { if (strcmp(bd_suffixes[i], bd_suffixes[j]) == 0 && i != j) { dev_err(&pdev->dev, "BD suffixes must be unique\n"); dev_err(&pdev->dev, "Found duplicate suffix: %s\n", bd_suffixes[i]); kfree(bd_suffixes); kfree(mfd_suffixes); return -EINVAL; } } } dev_info(&pdev->dev, "Found MFD suffix: %s\n", mfd_suffixes[i]); if (bd_suffixes[i]) dev_info(&pdev->dev, "Found BD suffix: %s\n", bd_suffixes[i]); } } else { dev_err(&pdev->dev, "cirrus_amp: failed to parse cirrus-amp DT\n"); return -EINVAL; } cirrus_amp_class = class_create(THIS_MODULE, CIRRUS_AMP_CLASS_NAME); if (IS_ERR(cirrus_amp_class)) { dev_err(&pdev->dev, "Failed to register cirrus amp class\n"); return -EINVAL; } cirrus_cal_init(cirrus_amp_class, num_amps, mfd_suffixes); cirrus_bd_init(cirrus_amp_class, num_amps, mfd_suffixes, bd_suffixes); cirrus_pwr_init(cirrus_amp_class, num_amps, mfd_suffixes); kfree(mfd_suffixes); return 0; } static int cirrus_amp_remove(struct platform_device *pdev) { cirrus_cal_exit(); cirrus_bd_exit(); cirrus_pwr_exit(); return 0; } static const struct of_device_id simple_of_match[] = { { .compatible = "cirrus-amp", }, {}, }; MODULE_DEVICE_TABLE(of, simple_of_match); static struct platform_driver cirrus_amp = { .driver = { .name = "cirrus-amp", .of_match_table = simple_of_match, }, .probe = cirrus_amp_probe, .remove = cirrus_amp_remove, }; module_platform_driver(cirrus_amp); MODULE_AUTHOR("David Rhodes "); MODULE_DESCRIPTION("Cirrus Amp driver"); MODULE_LICENSE("GPL");