linux/sound/soc/fsl/imx-audmux.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Freescale Semiconductor, Inc.
   3 * Copyright 2012 Linaro Ltd.
   4 * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
   5 *
   6 * Initial development of this code was funded by
   7 * Phytec Messtechnik GmbH, http://www.phytec.de
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License, or
  12 * (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 */
  19
  20#include <linux/clk.h>
  21#include <linux/debugfs.h>
  22#include <linux/err.h>
  23#include <linux/io.h>
  24#include <linux/module.h>
  25#include <linux/of.h>
  26#include <linux/of_device.h>
  27#include <linux/platform_device.h>
  28#include <linux/slab.h>
  29#include <linux/pinctrl/consumer.h>
  30
  31#include "imx-audmux.h"
  32
  33#define DRIVER_NAME "imx-audmux"
  34
  35static struct clk *audmux_clk;
  36static void __iomem *audmux_base;
  37
  38#define IMX_AUDMUX_V2_PTCR(x)           ((x) * 8)
  39#define IMX_AUDMUX_V2_PDCR(x)           ((x) * 8 + 4)
  40
  41#ifdef CONFIG_DEBUG_FS
  42static struct dentry *audmux_debugfs_root;
  43
  44/* There is an annoying discontinuity in the SSI numbering with regard
  45 * to the Linux number of the devices */
  46static const char *audmux_port_string(int port)
  47{
  48        switch (port) {
  49        case MX31_AUDMUX_PORT1_SSI0:
  50                return "imx-ssi.0";
  51        case MX31_AUDMUX_PORT2_SSI1:
  52                return "imx-ssi.1";
  53        case MX31_AUDMUX_PORT3_SSI_PINS_3:
  54                return "SSI3";
  55        case MX31_AUDMUX_PORT4_SSI_PINS_4:
  56                return "SSI4";
  57        case MX31_AUDMUX_PORT5_SSI_PINS_5:
  58                return "SSI5";
  59        case MX31_AUDMUX_PORT6_SSI_PINS_6:
  60                return "SSI6";
  61        default:
  62                return "UNKNOWN";
  63        }
  64}
  65
  66static ssize_t audmux_read_file(struct file *file, char __user *user_buf,
  67                                size_t count, loff_t *ppos)
  68{
  69        ssize_t ret;
  70        char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
  71        int port = (int)file->private_data;
  72        u32 pdcr, ptcr;
  73
  74        if (!buf)
  75                return -ENOMEM;
  76
  77        if (audmux_clk)
  78                clk_prepare_enable(audmux_clk);
  79
  80        ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port));
  81        pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port));
  82
  83        if (audmux_clk)
  84                clk_disable_unprepare(audmux_clk);
  85
  86        ret = snprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n",
  87                       pdcr, ptcr);
  88
  89        if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR)
  90                ret += snprintf(buf + ret, PAGE_SIZE - ret,
  91                                "TxFS output from %s, ",
  92                                audmux_port_string((ptcr >> 27) & 0x7));
  93        else
  94                ret += snprintf(buf + ret, PAGE_SIZE - ret,
  95                                "TxFS input, ");
  96
  97        if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR)
  98                ret += snprintf(buf + ret, PAGE_SIZE - ret,
  99                                "TxClk output from %s",
 100                                audmux_port_string((ptcr >> 22) & 0x7));
 101        else
 102                ret += snprintf(buf + ret, PAGE_SIZE - ret,
 103                                "TxClk input");
 104
 105        ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
 106
 107        if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) {
 108                ret += snprintf(buf + ret, PAGE_SIZE - ret,
 109                                "Port is symmetric");
 110        } else {
 111                if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR)
 112                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
 113                                        "RxFS output from %s, ",
 114                                        audmux_port_string((ptcr >> 17) & 0x7));
 115                else
 116                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
 117                                        "RxFS input, ");
 118
 119                if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR)
 120                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
 121                                        "RxClk output from %s",
 122                                        audmux_port_string((ptcr >> 12) & 0x7));
 123                else
 124                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
 125                                        "RxClk input");
 126        }
 127
 128        ret += snprintf(buf + ret, PAGE_SIZE - ret,
 129                        "\nData received from %s\n",
 130                        audmux_port_string((pdcr >> 13) & 0x7));
 131
 132        ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
 133
 134        kfree(buf);
 135
 136        return ret;
 137}
 138
 139static const struct file_operations audmux_debugfs_fops = {
 140        .open = simple_open,
 141        .read = audmux_read_file,
 142        .llseek = default_llseek,
 143};
 144
 145static void __init audmux_debugfs_init(void)
 146{
 147        int i;
 148        char buf[20];
 149
 150        audmux_debugfs_root = debugfs_create_dir("audmux", NULL);
 151        if (!audmux_debugfs_root) {
 152                pr_warning("Failed to create AUDMUX debugfs root\n");
 153                return;
 154        }
 155
 156        for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) {
 157                snprintf(buf, sizeof(buf), "ssi%d", i);
 158                if (!debugfs_create_file(buf, 0444, audmux_debugfs_root,
 159                                         (void *)i, &audmux_debugfs_fops))
 160                        pr_warning("Failed to create AUDMUX port %d debugfs file\n",
 161                                   i);
 162        }
 163}
 164
 165static void audmux_debugfs_remove(void)
 166{
 167        debugfs_remove_recursive(audmux_debugfs_root);
 168}
 169#else
 170static inline void audmux_debugfs_init(void)
 171{
 172}
 173
 174static inline void audmux_debugfs_remove(void)
 175{
 176}
 177#endif
 178
 179static enum imx_audmux_type {
 180        IMX21_AUDMUX,
 181        IMX31_AUDMUX,
 182} audmux_type;
 183
 184static struct platform_device_id imx_audmux_ids[] = {
 185        {
 186                .name = "imx21-audmux",
 187                .driver_data = IMX21_AUDMUX,
 188        }, {
 189                .name = "imx31-audmux",
 190                .driver_data = IMX31_AUDMUX,
 191        }, {
 192                /* sentinel */
 193        }
 194};
 195MODULE_DEVICE_TABLE(platform, imx_audmux_ids);
 196
 197static const struct of_device_id imx_audmux_dt_ids[] = {
 198        { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], },
 199        { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], },
 200        { /* sentinel */ }
 201};
 202MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids);
 203
 204static const uint8_t port_mapping[] = {
 205        0x0, 0x4, 0x8, 0x10, 0x14, 0x1c,
 206};
 207
 208int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr)
 209{
 210        if (audmux_type != IMX21_AUDMUX)
 211                return -EINVAL;
 212
 213        if (!audmux_base)
 214                return -ENOSYS;
 215
 216        if (port >= ARRAY_SIZE(port_mapping))
 217                return -EINVAL;
 218
 219        writel(pcr, audmux_base + port_mapping[port]);
 220
 221        return 0;
 222}
 223EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port);
 224
 225int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
 226                unsigned int pdcr)
 227{
 228        if (audmux_type != IMX31_AUDMUX)
 229                return -EINVAL;
 230
 231        if (!audmux_base)
 232                return -ENOSYS;
 233
 234        if (audmux_clk)
 235                clk_prepare_enable(audmux_clk);
 236
 237        writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port));
 238        writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port));
 239
 240        if (audmux_clk)
 241                clk_disable_unprepare(audmux_clk);
 242
 243        return 0;
 244}
 245EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port);
 246
 247static int imx_audmux_probe(struct platform_device *pdev)
 248{
 249        struct resource *res;
 250        struct pinctrl *pinctrl;
 251        const struct of_device_id *of_id =
 252                        of_match_device(imx_audmux_dt_ids, &pdev->dev);
 253
 254        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 255        audmux_base = devm_ioremap_resource(&pdev->dev, res);
 256        if (IS_ERR(audmux_base))
 257                return PTR_ERR(audmux_base);
 258
 259        pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
 260        if (IS_ERR(pinctrl)) {
 261                dev_err(&pdev->dev, "setup pinctrl failed!");
 262                return PTR_ERR(pinctrl);
 263        }
 264
 265        audmux_clk = clk_get(&pdev->dev, "audmux");
 266        if (IS_ERR(audmux_clk)) {
 267                dev_dbg(&pdev->dev, "cannot get clock: %ld\n",
 268                                PTR_ERR(audmux_clk));
 269                audmux_clk = NULL;
 270        }
 271
 272        if (of_id)
 273                pdev->id_entry = of_id->data;
 274        audmux_type = pdev->id_entry->driver_data;
 275        if (audmux_type == IMX31_AUDMUX)
 276                audmux_debugfs_init();
 277
 278        return 0;
 279}
 280
 281static int imx_audmux_remove(struct platform_device *pdev)
 282{
 283        if (audmux_type == IMX31_AUDMUX)
 284                audmux_debugfs_remove();
 285        clk_put(audmux_clk);
 286
 287        return 0;
 288}
 289
 290static struct platform_driver imx_audmux_driver = {
 291        .probe          = imx_audmux_probe,
 292        .remove         = imx_audmux_remove,
 293        .id_table       = imx_audmux_ids,
 294        .driver = {
 295                .name   = DRIVER_NAME,
 296                .owner  = THIS_MODULE,
 297                .of_match_table = imx_audmux_dt_ids,
 298        }
 299};
 300
 301static int __init imx_audmux_init(void)
 302{
 303        return platform_driver_register(&imx_audmux_driver);
 304}
 305subsys_initcall(imx_audmux_init);
 306
 307static void __exit imx_audmux_exit(void)
 308{
 309        platform_driver_unregister(&imx_audmux_driver);
 310}
 311module_exit(imx_audmux_exit);
 312
 313MODULE_DESCRIPTION("Freescale i.MX AUDMUX driver");
 314MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 315MODULE_LICENSE("GPL v2");
 316MODULE_ALIAS("platform:" DRIVER_NAME);
 317
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.