| /* |
| * Renesas R-Car SRC support |
| * |
| * Copyright (C) 2013 Renesas Solutions Corp. |
| * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| * |
| * 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 "rsnd.h" |
| |
| struct rsnd_src { |
| struct rsnd_src_platform_info *info; /* rcar_snd.h */ |
| struct rsnd_mod mod; |
| struct clk *clk; |
| }; |
| |
| #define RSND_SRC_NAME_SIZE 16 |
| |
| /* |
| * ADINR |
| */ |
| #define OTBL_24 (0 << 16) |
| #define OTBL_22 (2 << 16) |
| #define OTBL_20 (4 << 16) |
| #define OTBL_18 (6 << 16) |
| #define OTBL_16 (8 << 16) |
| |
| #define rsnd_src_mode_flags(p) ((p)->info->flags) |
| #define rsnd_src_convert_rate(p) ((p)->info->convert_rate) |
| #define rsnd_mod_to_src(_mod) \ |
| container_of((_mod), struct rsnd_src, mod) |
| #define rsnd_src_hpbif_is_enable(src) \ |
| (rsnd_src_mode_flags(src) & RSND_SCU_USE_HPBIF) |
| #define rsnd_src_dma_available(src) \ |
| rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod)) |
| |
| #define for_each_rsnd_src(pos, priv, i) \ |
| for ((i) = 0; \ |
| ((i) < rsnd_src_nr(priv)) && \ |
| ((pos) = (struct rsnd_src *)(priv)->src + i); \ |
| i++) |
| |
| |
| /* |
| * image of SRC (Sampling Rate Converter) |
| * |
| * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ |
| * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | |
| * 44.1kHz <-> +-----+ +-----+ +-------+ |
| * ... |
| * |
| */ |
| |
| /* |
| * src.c is caring... |
| * |
| * Gen1 |
| * |
| * [mem] -> [SRU] -> [SSI] |
| * |--------| |
| * |
| * Gen2 |
| * |
| * [mem] -> [SRC] -> [SSIU] -> [SSI] |
| * |-----------------| |
| */ |
| |
| /* |
| * How to use SRC bypass mode for debugging |
| * |
| * SRC has bypass mode, and it is useful for debugging. |
| * In Gen2 case, |
| * SRCm_MODE controls whether SRC is used or not |
| * SSI_MODE0 controls whether SSIU which receives SRC data |
| * is used or not. |
| * Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC, |
| * but SRC bypass mode needs SSI_MODE0 only. |
| * |
| * This driver request |
| * struct rsnd_src_platform_info { |
| * u32 flags; |
| * u32 convert_rate; |
| * } |
| * |
| * rsnd_src_hpbif_is_enable() will be true |
| * if flags had RSND_SRC_USE_HPBIF, |
| * and it controls whether SSIU is used or not. |
| * |
| * rsnd_src_convert_rate() indicates |
| * above convert_rate, and it controls |
| * whether SRC is used or not. |
| * |
| * ex) doesn't use SRC |
| * struct rsnd_src_platform_info info = { |
| * .flags = 0, |
| * .convert_rate = 0, |
| * }; |
| * |
| * ex) uses SRC |
| * struct rsnd_src_platform_info info = { |
| * .flags = RSND_SRC_USE_HPBIF, |
| * .convert_rate = 48000, |
| * }; |
| * |
| * ex) uses SRC bypass mode |
| * struct rsnd_src_platform_info info = { |
| * .flags = RSND_SRC_USE_HPBIF, |
| * .convert_rate = 0, |
| * }; |
| * |
| */ |
| |
| /* |
| * Gen1/Gen2 common functions |
| */ |
| int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); |
| struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); |
| struct rcar_snd_info *info = rsnd_priv_to_info(priv); |
| int ssi_id = rsnd_mod_id(ssi_mod); |
| int has_src = 0; |
| |
| /* |
| * SSI_MODE0 |
| */ |
| if (info->dai_info) { |
| has_src = !!src_mod; |
| } else { |
| struct rsnd_src *src = rsnd_mod_to_src(src_mod); |
| has_src = rsnd_src_hpbif_is_enable(src); |
| } |
| |
| rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id), |
| has_src ? 0 : (1 << ssi_id)); |
| |
| /* |
| * SSI_MODE1 |
| */ |
| if (rsnd_ssi_is_pin_sharing(ssi_mod)) { |
| int shift = -1; |
| switch (ssi_id) { |
| case 1: |
| shift = 0; |
| break; |
| case 2: |
| shift = 2; |
| break; |
| case 4: |
| shift = 16; |
| break; |
| } |
| |
| if (shift >= 0) |
| rsnd_mod_bset(ssi_mod, SSI_MODE1, |
| 0x3 << shift, |
| rsnd_dai_is_clk_master(rdai) ? |
| 0x2 << shift : 0x1 << shift); |
| } |
| |
| return 0; |
| } |
| |
| int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); |
| |
| /* enable PIO interrupt if Gen2 */ |
| if (rsnd_is_gen2(priv)) |
| rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); |
| |
| return 0; |
| } |
| |
| unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, |
| struct rsnd_dai_stream *io, |
| struct snd_pcm_runtime *runtime) |
| { |
| struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); |
| struct rsnd_src *src; |
| unsigned int rate = 0; |
| |
| if (src_mod) { |
| src = rsnd_mod_to_src(src_mod); |
| |
| /* |
| * return convert rate if SRC is used, |
| * otherwise, return runtime->rate as usual |
| */ |
| rate = rsnd_src_convert_rate(src); |
| } |
| |
| if (!rate) |
| rate = runtime->rate; |
| |
| return rate; |
| } |
| |
| static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| u32 convert_rate = rsnd_src_convert_rate(src); |
| u32 adinr = runtime->channels; |
| u32 fsrate = 0; |
| |
| if (convert_rate) |
| fsrate = 0x0400000 / convert_rate * runtime->rate; |
| |
| /* set/clear soft reset */ |
| rsnd_mod_write(mod, SRC_SWRSR, 0); |
| rsnd_mod_write(mod, SRC_SWRSR, 1); |
| |
| /* |
| * Initialize the operation of the SRC internal circuits |
| * see rsnd_src_start() |
| */ |
| rsnd_mod_write(mod, SRC_SRCIR, 1); |
| |
| /* Set channel number and output bit length */ |
| switch (runtime->sample_bits) { |
| case 16: |
| adinr |= OTBL_16; |
| break; |
| case 32: |
| adinr |= OTBL_24; |
| break; |
| default: |
| return -EIO; |
| } |
| rsnd_mod_write(mod, SRC_ADINR, adinr); |
| |
| /* Enable the initial value of IFS */ |
| if (fsrate) { |
| rsnd_mod_write(mod, SRC_IFSCR, 1); |
| |
| /* Set initial value of IFS */ |
| rsnd_mod_write(mod, SRC_IFSVR, fsrate); |
| } |
| |
| /* use DMA transfer */ |
| rsnd_mod_write(mod, SRC_BUSIF_MODE, 1); |
| |
| return 0; |
| } |
| |
| static int rsnd_src_init(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| |
| clk_enable(src->clk); |
| |
| return 0; |
| } |
| |
| static int rsnd_src_quit(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| |
| clk_disable(src->clk); |
| |
| return 0; |
| } |
| |
| static int rsnd_src_start(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| |
| /* |
| * Cancel the initialization and operate the SRC function |
| * see rsnd_src_set_convert_rate() |
| */ |
| rsnd_mod_write(mod, SRC_SRCIR, 0); |
| |
| if (rsnd_src_convert_rate(src)) |
| rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); |
| |
| return 0; |
| } |
| |
| |
| static int rsnd_src_stop(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| |
| if (rsnd_src_convert_rate(src)) |
| rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); |
| |
| return 0; |
| } |
| |
| static struct rsnd_mod_ops rsnd_src_non_ops = { |
| .name = "src (non)", |
| }; |
| |
| /* |
| * Gen1 functions |
| */ |
| static int rsnd_src_set_route_gen1(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct src_route_config { |
| u32 mask; |
| int shift; |
| } routes[] = { |
| { 0xF, 0, }, /* 0 */ |
| { 0xF, 4, }, /* 1 */ |
| { 0xF, 8, }, /* 2 */ |
| { 0x7, 12, }, /* 3 */ |
| { 0x7, 16, }, /* 4 */ |
| { 0x7, 20, }, /* 5 */ |
| { 0x7, 24, }, /* 6 */ |
| { 0x3, 28, }, /* 7 */ |
| { 0x3, 30, }, /* 8 */ |
| }; |
| u32 mask; |
| u32 val; |
| int id; |
| |
| id = rsnd_mod_id(mod); |
| if (id < 0 || id >= ARRAY_SIZE(routes)) |
| return -EIO; |
| |
| /* |
| * SRC_ROUTE_SELECT |
| */ |
| val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; |
| val = val << routes[id].shift; |
| mask = routes[id].mask << routes[id].shift; |
| |
| rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); |
| |
| return 0; |
| } |
| |
| static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
| u32 convert_rate = rsnd_src_convert_rate(src); |
| u32 mask; |
| u32 val; |
| int shift; |
| int id = rsnd_mod_id(mod); |
| int ret; |
| |
| /* |
| * SRC_TIMING_SELECT |
| */ |
| shift = (id % 4) * 8; |
| mask = 0x1F << shift; |
| |
| /* |
| * ADG is used as source clock if SRC was used, |
| * then, SSI WS is used as destination clock. |
| * SSI WS is used as source clock if SRC is not used |
| * (when playback, source/destination become reverse when capture) |
| */ |
| ret = 0; |
| if (convert_rate) { |
| /* use ADG */ |
| val = 0; |
| ret = rsnd_adg_set_convert_clk_gen1(priv, mod, |
| runtime->rate, |
| convert_rate); |
| } else if (8 == id) { |
| /* use SSI WS, but SRU8 is special */ |
| val = id << shift; |
| } else { |
| /* use SSI WS */ |
| val = (id + 1) << shift; |
| } |
| |
| if (ret < 0) |
| return ret; |
| |
| switch (id / 4) { |
| case 0: |
| rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); |
| break; |
| case 1: |
| rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); |
| break; |
| case 2: |
| rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| int ret; |
| |
| ret = rsnd_src_set_convert_rate(mod, rdai, io); |
| if (ret < 0) |
| return ret; |
| |
| /* Select SRC mode (fixed value) */ |
| rsnd_mod_write(mod, SRC_SRCCR, 0x00010110); |
| |
| /* Set the restriction value of the FS ratio (98%) */ |
| rsnd_mod_write(mod, SRC_MNFSR, |
| rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); |
| |
| /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ |
| |
| return 0; |
| } |
| |
| static int rsnd_src_init_gen1(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| int ret; |
| |
| ret = rsnd_src_init(mod, rdai, io); |
| if (ret < 0) |
| return ret; |
| |
| ret = rsnd_src_set_route_gen1(mod, rdai, io); |
| if (ret < 0) |
| return ret; |
| |
| ret = rsnd_src_set_convert_rate_gen1(mod, rdai, io); |
| if (ret < 0) |
| return ret; |
| |
| ret = rsnd_src_set_convert_timing_gen1(mod, rdai, io); |
| if (ret < 0) |
| return ret; |
| |
| return 0; |
| } |
| |
| static int rsnd_src_start_gen1(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| int id = rsnd_mod_id(mod); |
| |
| rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id)); |
| |
| return rsnd_src_start(mod, rdai, io); |
| } |
| |
| static int rsnd_src_stop_gen1(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| int id = rsnd_mod_id(mod); |
| |
| rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0); |
| |
| return rsnd_src_stop(mod, rdai, io); |
| } |
| |
| static struct rsnd_mod_ops rsnd_src_gen1_ops = { |
| .name = "sru (gen1)", |
| .init = rsnd_src_init_gen1, |
| .quit = rsnd_src_quit, |
| .start = rsnd_src_start_gen1, |
| .stop = rsnd_src_stop_gen1, |
| }; |
| |
| /* |
| * Gen2 functions |
| */ |
| static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| int ret; |
| |
| ret = rsnd_src_set_convert_rate(mod, rdai, io); |
| if (ret < 0) |
| return ret; |
| |
| rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_mod_read(mod, SRC_ADINR)); |
| rsnd_mod_write(mod, SSI_BUSIF_MODE, rsnd_mod_read(mod, SRC_BUSIF_MODE)); |
| |
| rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); |
| |
| rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); |
| rsnd_mod_write(mod, SRC_BSISR, 0x00100060); |
| |
| return 0; |
| } |
| |
| static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| u32 convert_rate = rsnd_src_convert_rate(src); |
| int ret; |
| |
| if (convert_rate) |
| ret = rsnd_adg_set_convert_clk_gen2(mod, rdai, io, |
| runtime->rate, |
| convert_rate); |
| else |
| ret = rsnd_adg_set_convert_timing_gen2(mod, rdai, io); |
| |
| return ret; |
| } |
| |
| static int rsnd_src_probe_gen2(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
| struct rcar_snd_info *info = rsnd_priv_to_info(priv); |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, rsnd_mod_id(mod)); |
| struct device *dev = rsnd_priv_to_dev(priv); |
| int ret; |
| int is_play; |
| |
| if (info->dai_info) |
| is_play = rsnd_info_is_playback(priv, src); |
| else |
| is_play = rsnd_ssi_is_play(ssi); |
| |
| ret = rsnd_dma_init(priv, |
| rsnd_mod_to_dma(mod), |
| is_play, |
| src->info->dma_id); |
| if (ret < 0) |
| dev_err(dev, "SRC DMA failed\n"); |
| |
| return ret; |
| } |
| |
| static int rsnd_src_remove_gen2(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); |
| |
| return 0; |
| } |
| |
| static int rsnd_src_init_gen2(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| int ret; |
| |
| ret = rsnd_src_init(mod, rdai, io); |
| if (ret < 0) |
| return ret; |
| |
| ret = rsnd_src_set_convert_rate_gen2(mod, rdai, io); |
| if (ret < 0) |
| return ret; |
| |
| ret = rsnd_src_set_convert_timing_gen2(mod, rdai, io); |
| if (ret < 0) |
| return ret; |
| |
| return 0; |
| } |
| |
| static int rsnd_src_start_gen2(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| |
| rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); |
| |
| rsnd_mod_write(mod, SSI_CTRL, 0x1); |
| rsnd_mod_write(mod, SRC_CTRL, 0x11); |
| |
| return rsnd_src_start(mod, rdai, io); |
| } |
| |
| static int rsnd_src_stop_gen2(struct rsnd_mod *mod, |
| struct rsnd_dai *rdai, |
| struct rsnd_dai_stream *io) |
| { |
| struct rsnd_src *src = rsnd_mod_to_src(mod); |
| |
| rsnd_mod_write(mod, SSI_CTRL, 0); |
| rsnd_mod_write(mod, SRC_CTRL, 0); |
| |
| rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); |
| |
| return rsnd_src_stop(mod, rdai, io); |
| } |
| |
| static struct rsnd_mod_ops rsnd_src_gen2_ops = { |
| .name = "src (gen2)", |
| .probe = rsnd_src_probe_gen2, |
| .remove = rsnd_src_remove_gen2, |
| .init = rsnd_src_init_gen2, |
| .quit = rsnd_src_quit, |
| .start = rsnd_src_start_gen2, |
| .stop = rsnd_src_stop_gen2, |
| }; |
| |
| struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) |
| { |
| if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) |
| id = 0; |
| |
| return &((struct rsnd_src *)(priv->src) + id)->mod; |
| } |
| |
| static void rsnd_of_parse_src(struct platform_device *pdev, |
| const struct rsnd_of_data *of_data, |
| struct rsnd_priv *priv) |
| { |
| struct device_node *src_node; |
| struct rcar_snd_info *info = rsnd_priv_to_info(priv); |
| struct rsnd_src_platform_info *src_info; |
| struct device *dev = &pdev->dev; |
| int nr; |
| |
| if (!of_data) |
| return; |
| |
| src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); |
| if (!src_node) |
| return; |
| |
| nr = of_get_child_count(src_node); |
| if (!nr) |
| return; |
| |
| src_info = devm_kzalloc(dev, |
| sizeof(struct rsnd_src_platform_info) * nr, |
| GFP_KERNEL); |
| if (!src_info) { |
| dev_err(dev, "src info allocation error\n"); |
| return; |
| } |
| |
| info->src_info = src_info; |
| info->src_info_nr = nr; |
| } |
| |
| int rsnd_src_probe(struct platform_device *pdev, |
| const struct rsnd_of_data *of_data, |
| struct rsnd_priv *priv) |
| { |
| struct rcar_snd_info *info = rsnd_priv_to_info(priv); |
| struct device *dev = rsnd_priv_to_dev(priv); |
| struct rsnd_src *src; |
| struct rsnd_mod_ops *ops; |
| struct clk *clk; |
| char name[RSND_SRC_NAME_SIZE]; |
| int i, nr; |
| |
| rsnd_of_parse_src(pdev, of_data, priv); |
| |
| /* |
| * init SRC |
| */ |
| nr = info->src_info_nr; |
| if (!nr) |
| return 0; |
| |
| src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); |
| if (!src) { |
| dev_err(dev, "SRC allocate failed\n"); |
| return -ENOMEM; |
| } |
| |
| priv->src_nr = nr; |
| priv->src = src; |
| |
| for_each_rsnd_src(src, priv, i) { |
| snprintf(name, RSND_SRC_NAME_SIZE, "src.%d", i); |
| |
| clk = devm_clk_get(dev, name); |
| if (IS_ERR(clk)) { |
| snprintf(name, RSND_SRC_NAME_SIZE, "scu.%d", i); |
| clk = devm_clk_get(dev, name); |
| } |
| |
| if (IS_ERR(clk)) |
| return PTR_ERR(clk); |
| |
| src->info = &info->src_info[i]; |
| src->clk = clk; |
| |
| ops = &rsnd_src_non_ops; |
| if (rsnd_src_hpbif_is_enable(src)) { |
| if (rsnd_is_gen1(priv)) |
| ops = &rsnd_src_gen1_ops; |
| if (rsnd_is_gen2(priv)) |
| ops = &rsnd_src_gen2_ops; |
| } |
| |
| rsnd_mod_init(priv, &src->mod, ops, RSND_MOD_SRC, i); |
| |
| dev_dbg(dev, "SRC%d probed\n", i); |
| } |
| |
| return 0; |
| } |