1/* 2 * ctu.c 3 * 4 * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10#include "rsnd.h" 11 12#define CTU_NAME_SIZE 16 13#define CTU_NAME "ctu" 14 15struct rsnd_ctu { 16 struct rsnd_ctu_platform_info *info; /* rcar_snd.h */ 17 struct rsnd_mod mod; 18}; 19 20#define rsnd_ctu_nr(priv) ((priv)->ctu_nr) 21#define for_each_rsnd_ctu(pos, priv, i) \ 22 for ((i) = 0; \ 23 ((i) < rsnd_ctu_nr(priv)) && \ 24 ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ 25 i++) 26 27#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) 28#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) 29static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) 30{ 31 rsnd_mod_write(mod, CTU_CTUIR, enable); 32} 33 34static int rsnd_ctu_init(struct rsnd_mod *mod, 35 struct rsnd_dai_stream *io, 36 struct rsnd_priv *priv) 37{ 38 rsnd_mod_power_on(mod); 39 40 rsnd_ctu_initialize_lock(mod); 41 42 rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); 43 44 rsnd_ctu_initialize_unlock(mod); 45 46 return 0; 47} 48 49static int rsnd_ctu_quit(struct rsnd_mod *mod, 50 struct rsnd_dai_stream *io, 51 struct rsnd_priv *priv) 52{ 53 rsnd_mod_power_off(mod); 54 55 return 0; 56} 57 58static struct rsnd_mod_ops rsnd_ctu_ops = { 59 .name = CTU_NAME, 60 .init = rsnd_ctu_init, 61 .quit = rsnd_ctu_quit, 62}; 63 64struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) 65{ 66 if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) 67 id = 0; 68 69 return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id); 70} 71 72static void rsnd_of_parse_ctu(struct platform_device *pdev, 73 const struct rsnd_of_data *of_data, 74 struct rsnd_priv *priv) 75{ 76 struct device_node *node; 77 struct rsnd_ctu_platform_info *ctu_info; 78 struct rcar_snd_info *info = rsnd_priv_to_info(priv); 79 struct device *dev = &pdev->dev; 80 int nr; 81 82 if (!of_data) 83 return; 84 85 node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu"); 86 if (!node) 87 return; 88 89 nr = of_get_child_count(node); 90 if (!nr) 91 goto rsnd_of_parse_ctu_end; 92 93 ctu_info = devm_kzalloc(dev, 94 sizeof(struct rsnd_ctu_platform_info) * nr, 95 GFP_KERNEL); 96 if (!ctu_info) { 97 dev_err(dev, "ctu info allocation error\n"); 98 goto rsnd_of_parse_ctu_end; 99 } 100 101 info->ctu_info = ctu_info; 102 info->ctu_info_nr = nr; 103 104rsnd_of_parse_ctu_end: 105 of_node_put(node); 106 107} 108 109int rsnd_ctu_probe(struct platform_device *pdev, 110 const struct rsnd_of_data *of_data, 111 struct rsnd_priv *priv) 112{ 113 struct rcar_snd_info *info = rsnd_priv_to_info(priv); 114 struct device *dev = rsnd_priv_to_dev(priv); 115 struct rsnd_ctu *ctu; 116 struct clk *clk; 117 char name[CTU_NAME_SIZE]; 118 int i, nr, ret; 119 120 /* This driver doesn't support Gen1 at this point */ 121 if (rsnd_is_gen1(priv)) 122 return 0; 123 124 rsnd_of_parse_ctu(pdev, of_data, priv); 125 126 nr = info->ctu_info_nr; 127 if (!nr) 128 return 0; 129 130 ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL); 131 if (!ctu) 132 return -ENOMEM; 133 134 priv->ctu_nr = nr; 135 priv->ctu = ctu; 136 137 for_each_rsnd_ctu(ctu, priv, i) { 138 /* 139 * CTU00, CTU01, CTU02, CTU03 => CTU0 140 * CTU10, CTU11, CTU12, CTU13 => CTU1 141 */ 142 snprintf(name, CTU_NAME_SIZE, "%s.%d", 143 CTU_NAME, i / 4); 144 145 clk = devm_clk_get(dev, name); 146 if (IS_ERR(clk)) 147 return PTR_ERR(clk); 148 149 ctu->info = &info->ctu_info[i]; 150 151 ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, 152 clk, RSND_MOD_CTU, i); 153 if (ret) 154 return ret; 155 } 156 157 return 0; 158} 159 160void rsnd_ctu_remove(struct platform_device *pdev, 161 struct rsnd_priv *priv) 162{ 163 struct rsnd_ctu *ctu; 164 int i; 165 166 for_each_rsnd_ctu(ctu, priv, i) { 167 rsnd_mod_quit(rsnd_mod_get(ctu)); 168 } 169} 170