| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1 | /* | 
|  | 2 | *  Asihpi soundcard | 
|  | 3 | *  Copyright (c) by AudioScience Inc <alsa@audioscience.com> | 
|  | 4 | * | 
|  | 5 | *   This program is free software; you can redistribute it and/or modify | 
|  | 6 | *   it under the terms of version 2 of the GNU General Public License as | 
|  | 7 | *   published by the Free Software Foundation; | 
|  | 8 | * | 
|  | 9 | *   This program is distributed in the hope that it will be useful, | 
|  | 10 | *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11 | *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 12 | *   GNU General Public License for more details. | 
|  | 13 | * | 
|  | 14 | *   You should have received a copy of the GNU General Public License | 
|  | 15 | *   along with this program; if not, write to the Free Software | 
|  | 16 | *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
|  | 17 | * | 
|  | 18 | * | 
|  | 19 | *  The following is not a condition of use, merely a request: | 
|  | 20 | *  If you modify this program, particularly if you fix errors, AudioScience Inc | 
|  | 21 | *  would appreciate it if you grant us the right to use those modifications | 
|  | 22 | *  for any purpose including commercial applications. | 
|  | 23 | */ | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 24 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 25 | #include "hpi_internal.h" | 
| Eliot Blennerhassett | f50efa2 | 2011-12-22 13:38:48 +1300 | [diff] [blame] | 26 | #include "hpi_version.h" | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 27 | #include "hpimsginit.h" | 
|  | 28 | #include "hpioctl.h" | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 29 | #include "hpicmn.h" | 
|  | 30 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 31 |  | 
|  | 32 | #include <linux/pci.h> | 
|  | 33 | #include <linux/init.h> | 
|  | 34 | #include <linux/jiffies.h> | 
|  | 35 | #include <linux/slab.h> | 
|  | 36 | #include <linux/time.h> | 
|  | 37 | #include <linux/wait.h> | 
| Paul Gortmaker | da155d5 | 2011-07-15 12:38:28 -0400 | [diff] [blame] | 38 | #include <linux/module.h> | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 39 | #include <sound/core.h> | 
|  | 40 | #include <sound/control.h> | 
|  | 41 | #include <sound/pcm.h> | 
|  | 42 | #include <sound/pcm_params.h> | 
|  | 43 | #include <sound/info.h> | 
|  | 44 | #include <sound/initval.h> | 
|  | 45 | #include <sound/tlv.h> | 
|  | 46 | #include <sound/hwdep.h> | 
|  | 47 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 48 | MODULE_LICENSE("GPL"); | 
|  | 49 | MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>"); | 
| Eliot Blennerhassett | f50efa2 | 2011-12-22 13:38:48 +1300 | [diff] [blame] | 50 | MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx " | 
|  | 51 | HPI_VER_STRING); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 52 |  | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 53 | #if defined CONFIG_SND_DEBUG_VERBOSE | 
|  | 54 | /** | 
|  | 55 | * snd_printddd - very verbose debug printk | 
|  | 56 | * @format: format string | 
|  | 57 | * | 
|  | 58 | * Works like snd_printk() for debugging purposes. | 
|  | 59 | * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set. | 
|  | 60 | * Must set snd module debug parameter to 3 to enable at runtime. | 
|  | 61 | */ | 
|  | 62 | #define snd_printddd(format, args...) \ | 
|  | 63 | __snd_printk(3, __FILE__, __LINE__, format, ##args) | 
|  | 64 | #else | 
| Eliot Blennerhassett | b0096a6 | 2011-04-05 20:55:46 +1200 | [diff] [blame] | 65 | #define snd_printddd(format, args...) do { } while (0) | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 66 | #endif | 
|  | 67 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 68 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* index 0-MAX */ | 
|  | 69 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */ | 
| Rusty Russell | a67ff6a | 2011-12-15 13:49:36 +1030 | [diff] [blame] | 70 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; | 
|  | 71 | static bool enable_hpi_hwdep = 1; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 72 |  | 
|  | 73 | module_param_array(index, int, NULL, S_IRUGO); | 
|  | 74 | MODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard."); | 
|  | 75 |  | 
|  | 76 | module_param_array(id, charp, NULL, S_IRUGO); | 
|  | 77 | MODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard."); | 
|  | 78 |  | 
|  | 79 | module_param_array(enable, bool, NULL, S_IRUGO); | 
|  | 80 | MODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard."); | 
|  | 81 |  | 
|  | 82 | module_param(enable_hpi_hwdep, bool, S_IRUGO|S_IWUSR); | 
|  | 83 | MODULE_PARM_DESC(enable_hpi_hwdep, | 
|  | 84 | "ALSA enable HPI hwdep for AudioScience soundcard "); | 
|  | 85 |  | 
|  | 86 | /* identify driver */ | 
|  | 87 | #ifdef KERNEL_ALSA_BUILD | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 88 | static char *build_info = "Built using headers from kernel source"; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 89 | module_param(build_info, charp, S_IRUGO); | 
|  | 90 | MODULE_PARM_DESC(build_info, "built using headers from kernel source"); | 
|  | 91 | #else | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 92 | static char *build_info = "Built within ALSA source"; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 93 | module_param(build_info, charp, S_IRUGO); | 
|  | 94 | MODULE_PARM_DESC(build_info, "built within ALSA source"); | 
|  | 95 | #endif | 
|  | 96 |  | 
|  | 97 | /* set to 1 to dump every control from adapter to log */ | 
|  | 98 | static const int mixer_dump; | 
|  | 99 |  | 
|  | 100 | #define DEFAULT_SAMPLERATE 44100 | 
|  | 101 | static int adapter_fs = DEFAULT_SAMPLERATE; | 
|  | 102 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 103 | /* defaults */ | 
|  | 104 | #define PERIODS_MIN 2 | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 105 | #define PERIOD_BYTES_MIN  2048 | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 106 | #define BUFFER_BYTES_MAX (512 * 1024) | 
|  | 107 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 108 | #define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7) | 
|  | 109 |  | 
|  | 110 | struct clk_source { | 
|  | 111 | int source; | 
|  | 112 | int index; | 
|  | 113 | char *name; | 
|  | 114 | }; | 
|  | 115 |  | 
|  | 116 | struct clk_cache { | 
|  | 117 | int count; | 
|  | 118 | int has_local; | 
|  | 119 | struct clk_source s[MAX_CLOCKSOURCES]; | 
|  | 120 | }; | 
|  | 121 |  | 
|  | 122 | /* Per card data */ | 
|  | 123 | struct snd_card_asihpi { | 
|  | 124 | struct snd_card *card; | 
|  | 125 | struct pci_dev *pci; | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 126 | struct hpi_adapter *hpi; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 127 |  | 
|  | 128 | u32 h_mixer; | 
|  | 129 | struct clk_cache cc; | 
|  | 130 |  | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 131 | u16 can_dma; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 132 | u16 support_grouping; | 
|  | 133 | u16 support_mrx; | 
|  | 134 | u16 update_interval_frames; | 
|  | 135 | u16 in_max_chans; | 
|  | 136 | u16 out_max_chans; | 
| Eliot Blennerhassett | c382a5d | 2011-12-22 13:38:33 +1300 | [diff] [blame] | 137 | u16 in_min_chans; | 
|  | 138 | u16 out_min_chans; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 139 | }; | 
|  | 140 |  | 
|  | 141 | /* Per stream data */ | 
|  | 142 | struct snd_card_asihpi_pcm { | 
|  | 143 | struct timer_list timer; | 
|  | 144 | unsigned int respawn_timer; | 
|  | 145 | unsigned int hpi_buffer_attached; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 146 | unsigned int buffer_bytes; | 
|  | 147 | unsigned int period_bytes; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 148 | unsigned int bytes_per_sec; | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 149 | unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */ | 
|  | 150 | unsigned int pcm_buf_dma_ofs;	/* DMA R/W offset in buffer */ | 
|  | 151 | unsigned int pcm_buf_elapsed_dma_ofs;	/* DMA R/W offset in buffer */ | 
| Eliot Blennerhassett | 0b7ce9e | 2011-04-05 20:55:43 +1200 | [diff] [blame] | 152 | unsigned int drained_count; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 153 | struct snd_pcm_substream *substream; | 
|  | 154 | u32 h_stream; | 
|  | 155 | struct hpi_format format; | 
|  | 156 | }; | 
|  | 157 |  | 
|  | 158 | /* universal stream verbs work with out or in stream handles */ | 
|  | 159 |  | 
|  | 160 | /* Functions to allow driver to give a buffer to HPI for busmastering */ | 
|  | 161 |  | 
|  | 162 | static u16 hpi_stream_host_buffer_attach( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 163 | u32 h_stream,   /* handle to outstream. */ | 
|  | 164 | u32 size_in_bytes, /* size in bytes of bus mastering buffer */ | 
|  | 165 | u32 pci_address | 
|  | 166 | ) | 
|  | 167 | { | 
|  | 168 | struct hpi_message hm; | 
|  | 169 | struct hpi_response hr; | 
|  | 170 | unsigned int obj = hpi_handle_object(h_stream); | 
|  | 171 |  | 
|  | 172 | if (!h_stream) | 
|  | 173 | return HPI_ERROR_INVALID_OBJ; | 
|  | 174 | hpi_init_message_response(&hm, &hr, obj, | 
|  | 175 | obj == HPI_OBJ_OSTREAM ? | 
|  | 176 | HPI_OSTREAM_HOSTBUFFER_ALLOC : | 
|  | 177 | HPI_ISTREAM_HOSTBUFFER_ALLOC); | 
|  | 178 |  | 
|  | 179 | hpi_handle_to_indexes(h_stream, &hm.adapter_index, | 
|  | 180 | &hm.obj_index); | 
|  | 181 |  | 
|  | 182 | hm.u.d.u.buffer.buffer_size = size_in_bytes; | 
|  | 183 | hm.u.d.u.buffer.pci_address = pci_address; | 
|  | 184 | hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER; | 
|  | 185 | hpi_send_recv(&hm, &hr); | 
|  | 186 | return hr.error; | 
|  | 187 | } | 
|  | 188 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 189 | static u16 hpi_stream_host_buffer_detach(u32  h_stream) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 190 | { | 
|  | 191 | struct hpi_message hm; | 
|  | 192 | struct hpi_response hr; | 
|  | 193 | unsigned int obj = hpi_handle_object(h_stream); | 
|  | 194 |  | 
|  | 195 | if (!h_stream) | 
|  | 196 | return HPI_ERROR_INVALID_OBJ; | 
|  | 197 |  | 
|  | 198 | hpi_init_message_response(&hm, &hr,  obj, | 
|  | 199 | obj == HPI_OBJ_OSTREAM ? | 
|  | 200 | HPI_OSTREAM_HOSTBUFFER_FREE : | 
|  | 201 | HPI_ISTREAM_HOSTBUFFER_FREE); | 
|  | 202 |  | 
|  | 203 | hpi_handle_to_indexes(h_stream, &hm.adapter_index, | 
|  | 204 | &hm.obj_index); | 
|  | 205 | hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER; | 
|  | 206 | hpi_send_recv(&hm, &hr); | 
|  | 207 | return hr.error; | 
|  | 208 | } | 
|  | 209 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 210 | static inline u16 hpi_stream_start(u32 h_stream) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 211 | { | 
|  | 212 | if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 213 | return hpi_outstream_start(h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 214 | else | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 215 | return hpi_instream_start(h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 216 | } | 
|  | 217 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 218 | static inline u16 hpi_stream_stop(u32 h_stream) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 219 | { | 
|  | 220 | if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 221 | return hpi_outstream_stop(h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 222 | else | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 223 | return hpi_instream_stop(h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 224 | } | 
|  | 225 |  | 
|  | 226 | static inline u16 hpi_stream_get_info_ex( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 227 | u32 h_stream, | 
|  | 228 | u16        *pw_state, | 
|  | 229 | u32        *pbuffer_size, | 
|  | 230 | u32        *pdata_in_buffer, | 
|  | 231 | u32        *psample_count, | 
|  | 232 | u32        *pauxiliary_data | 
|  | 233 | ) | 
|  | 234 | { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 235 | u16 e; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 236 | if (hpi_handle_object(h_stream)  ==  HPI_OBJ_OSTREAM) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 237 | e = hpi_outstream_get_info_ex(h_stream, pw_state, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 238 | pbuffer_size, pdata_in_buffer, | 
|  | 239 | psample_count, pauxiliary_data); | 
|  | 240 | else | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 241 | e = hpi_instream_get_info_ex(h_stream, pw_state, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 242 | pbuffer_size, pdata_in_buffer, | 
|  | 243 | psample_count, pauxiliary_data); | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 244 | return e; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 245 | } | 
|  | 246 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 247 | static inline u16 hpi_stream_group_add( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 248 | u32 h_master, | 
|  | 249 | u32 h_stream) | 
|  | 250 | { | 
|  | 251 | if (hpi_handle_object(h_master) ==  HPI_OBJ_OSTREAM) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 252 | return hpi_outstream_group_add(h_master, h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 253 | else | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 254 | return hpi_instream_group_add(h_master, h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 255 | } | 
|  | 256 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 257 | static inline u16 hpi_stream_group_reset(u32 h_stream) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 258 | { | 
|  | 259 | if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 260 | return hpi_outstream_group_reset(h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 261 | else | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 262 | return hpi_instream_group_reset(h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 263 | } | 
|  | 264 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 265 | static inline u16 hpi_stream_group_get_map( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 266 | u32 h_stream, u32 *mo, u32 *mi) | 
|  | 267 | { | 
|  | 268 | if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 269 | return hpi_outstream_group_get_map(h_stream, mo, mi); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 270 | else | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 271 | return hpi_instream_group_get_map(h_stream, mo, mi); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 272 | } | 
|  | 273 |  | 
|  | 274 | static u16 handle_error(u16 err, int line, char *filename) | 
|  | 275 | { | 
|  | 276 | if (err) | 
|  | 277 | printk(KERN_WARNING | 
|  | 278 | "in file %s, line %d: HPI error %d\n", | 
|  | 279 | filename, line, err); | 
|  | 280 | return err; | 
|  | 281 | } | 
|  | 282 |  | 
|  | 283 | #define hpi_handle_error(x)  handle_error(x, __LINE__, __FILE__) | 
|  | 284 |  | 
|  | 285 | /***************************** GENERAL PCM ****************/ | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 286 |  | 
|  | 287 | static void print_hwparams(struct snd_pcm_substream *substream, | 
|  | 288 | struct snd_pcm_hw_params *p) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 289 | { | 
| Eliot Blennerhassett | 0a17e99 | 2011-07-22 15:52:44 +1200 | [diff] [blame] | 290 | char name[16]; | 
|  | 291 | snd_pcm_debug_name(substream, name, sizeof(name)); | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 292 | snd_printd("%s HWPARAMS\n", name); | 
|  | 293 | snd_printd(" samplerate %d Hz\n", params_rate(p)); | 
|  | 294 | snd_printd(" channels %d\n", params_channels(p)); | 
|  | 295 | snd_printd(" format %d\n", params_format(p)); | 
|  | 296 | snd_printd(" subformat %d\n", params_subformat(p)); | 
|  | 297 | snd_printd(" buffer %d B\n", params_buffer_bytes(p)); | 
|  | 298 | snd_printd(" period %d B\n", params_period_bytes(p)); | 
|  | 299 | snd_printd(" access %d\n", params_access(p)); | 
|  | 300 | snd_printd(" period_size %d\n", params_period_size(p)); | 
|  | 301 | snd_printd(" periods %d\n", params_periods(p)); | 
|  | 302 | snd_printd(" buffer_size %d\n", params_buffer_size(p)); | 
|  | 303 | snd_printd(" %d B/s\n", params_rate(p) * | 
|  | 304 | params_channels(p) * | 
|  | 305 | snd_pcm_format_width(params_format(p)) / 8); | 
|  | 306 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 307 | } | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 308 |  | 
|  | 309 | static snd_pcm_format_t hpi_to_alsa_formats[] = { | 
|  | 310 | -1,			/* INVALID */ | 
|  | 311 | SNDRV_PCM_FORMAT_U8,	/* HPI_FORMAT_PCM8_UNSIGNED        1 */ | 
|  | 312 | SNDRV_PCM_FORMAT_S16,	/* HPI_FORMAT_PCM16_SIGNED         2 */ | 
|  | 313 | -1,			/* HPI_FORMAT_MPEG_L1              3 */ | 
|  | 314 | SNDRV_PCM_FORMAT_MPEG,	/* HPI_FORMAT_MPEG_L2              4 */ | 
|  | 315 | SNDRV_PCM_FORMAT_MPEG,	/* HPI_FORMAT_MPEG_L3              5 */ | 
|  | 316 | -1,			/* HPI_FORMAT_DOLBY_AC2            6 */ | 
|  | 317 | -1,			/* HPI_FORMAT_DOLBY_AC3            7 */ | 
|  | 318 | SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN      8 */ | 
|  | 319 | -1,			/* HPI_FORMAT_AA_TAGIT1_HITS       9 */ | 
|  | 320 | -1,			/* HPI_FORMAT_AA_TAGIT1_INSERTS   10 */ | 
|  | 321 | SNDRV_PCM_FORMAT_S32,	/* HPI_FORMAT_PCM32_SIGNED        11 */ | 
|  | 322 | -1,			/* HPI_FORMAT_RAW_BITSTREAM       12 */ | 
|  | 323 | -1,			/* HPI_FORMAT_AA_TAGIT1_HITS_EX1  13 */ | 
|  | 324 | SNDRV_PCM_FORMAT_FLOAT,	/* HPI_FORMAT_PCM32_FLOAT         14 */ | 
|  | 325 | #if 1 | 
|  | 326 | /* ALSA can't handle 3 byte sample size together with power-of-2 | 
|  | 327 | *  constraint on buffer_bytes, so disable this format | 
|  | 328 | */ | 
|  | 329 | -1 | 
|  | 330 | #else | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 331 | /* SNDRV_PCM_FORMAT_S24_3LE */ /* HPI_FORMAT_PCM24_SIGNED 15 */ | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 332 | #endif | 
|  | 333 | }; | 
|  | 334 |  | 
|  | 335 |  | 
|  | 336 | static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format, | 
|  | 337 | u16 *hpi_format) | 
|  | 338 | { | 
|  | 339 | u16 format; | 
|  | 340 |  | 
|  | 341 | for (format = HPI_FORMAT_PCM8_UNSIGNED; | 
|  | 342 | format <= HPI_FORMAT_PCM24_SIGNED; format++) { | 
|  | 343 | if (hpi_to_alsa_formats[format] == alsa_format) { | 
|  | 344 | *hpi_format = format; | 
|  | 345 | return 0; | 
|  | 346 | } | 
|  | 347 | } | 
|  | 348 |  | 
|  | 349 | snd_printd(KERN_WARNING "failed match for alsa format %d\n", | 
|  | 350 | alsa_format); | 
|  | 351 | *hpi_format = 0; | 
|  | 352 | return -EINVAL; | 
|  | 353 | } | 
|  | 354 |  | 
|  | 355 | static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi, | 
|  | 356 | struct snd_pcm_hardware *pcmhw) | 
|  | 357 | { | 
|  | 358 | u16 err; | 
|  | 359 | u32 h_control; | 
|  | 360 | u32 sample_rate; | 
|  | 361 | int idx; | 
|  | 362 | unsigned int rate_min = 200000; | 
|  | 363 | unsigned int rate_max = 0; | 
|  | 364 | unsigned int rates = 0; | 
|  | 365 |  | 
|  | 366 | if (asihpi->support_mrx) { | 
|  | 367 | rates |= SNDRV_PCM_RATE_CONTINUOUS; | 
|  | 368 | rates |= SNDRV_PCM_RATE_8000_96000; | 
|  | 369 | rate_min = 8000; | 
|  | 370 | rate_max = 100000; | 
|  | 371 | } else { | 
|  | 372 | /* on cards without SRC, | 
|  | 373 | valid rates are determined by sampleclock */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 374 | err = hpi_mixer_get_control(asihpi->h_mixer, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 375 | HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, | 
|  | 376 | HPI_CONTROL_SAMPLECLOCK, &h_control); | 
|  | 377 | if (err) { | 
|  | 378 | snd_printk(KERN_ERR | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 379 | "No local sampleclock, err %d\n", err); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 380 | } | 
|  | 381 |  | 
| Eliot Blennerhassett | 7bf76c3 | 2011-03-25 15:25:46 +1300 | [diff] [blame] | 382 | for (idx = -1; idx < 100; idx++) { | 
|  | 383 | if (idx == -1) { | 
|  | 384 | if (hpi_sample_clock_get_sample_rate(h_control, | 
|  | 385 | &sample_rate)) | 
|  | 386 | continue; | 
|  | 387 | } else if (hpi_sample_clock_query_local_rate(h_control, | 
|  | 388 | idx, &sample_rate)) { | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 389 | break; | 
|  | 390 | } | 
|  | 391 |  | 
|  | 392 | rate_min = min(rate_min, sample_rate); | 
|  | 393 | rate_max = max(rate_max, sample_rate); | 
|  | 394 |  | 
|  | 395 | switch (sample_rate) { | 
|  | 396 | case 5512: | 
|  | 397 | rates |= SNDRV_PCM_RATE_5512; | 
|  | 398 | break; | 
|  | 399 | case 8000: | 
|  | 400 | rates |= SNDRV_PCM_RATE_8000; | 
|  | 401 | break; | 
|  | 402 | case 11025: | 
|  | 403 | rates |= SNDRV_PCM_RATE_11025; | 
|  | 404 | break; | 
|  | 405 | case 16000: | 
|  | 406 | rates |= SNDRV_PCM_RATE_16000; | 
|  | 407 | break; | 
|  | 408 | case 22050: | 
|  | 409 | rates |= SNDRV_PCM_RATE_22050; | 
|  | 410 | break; | 
|  | 411 | case 32000: | 
|  | 412 | rates |= SNDRV_PCM_RATE_32000; | 
|  | 413 | break; | 
|  | 414 | case 44100: | 
|  | 415 | rates |= SNDRV_PCM_RATE_44100; | 
|  | 416 | break; | 
|  | 417 | case 48000: | 
|  | 418 | rates |= SNDRV_PCM_RATE_48000; | 
|  | 419 | break; | 
|  | 420 | case 64000: | 
|  | 421 | rates |= SNDRV_PCM_RATE_64000; | 
|  | 422 | break; | 
|  | 423 | case 88200: | 
|  | 424 | rates |= SNDRV_PCM_RATE_88200; | 
|  | 425 | break; | 
|  | 426 | case 96000: | 
|  | 427 | rates |= SNDRV_PCM_RATE_96000; | 
|  | 428 | break; | 
|  | 429 | case 176400: | 
|  | 430 | rates |= SNDRV_PCM_RATE_176400; | 
|  | 431 | break; | 
|  | 432 | case 192000: | 
|  | 433 | rates |= SNDRV_PCM_RATE_192000; | 
|  | 434 | break; | 
|  | 435 | default: /* some other rate */ | 
|  | 436 | rates |= SNDRV_PCM_RATE_KNOT; | 
|  | 437 | } | 
|  | 438 | } | 
|  | 439 | } | 
|  | 440 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 441 | pcmhw->rates = rates; | 
|  | 442 | pcmhw->rate_min = rate_min; | 
|  | 443 | pcmhw->rate_max = rate_max; | 
|  | 444 | } | 
|  | 445 |  | 
|  | 446 | static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, | 
|  | 447 | struct snd_pcm_hw_params *params) | 
|  | 448 | { | 
|  | 449 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 450 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 451 | struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); | 
|  | 452 | int err; | 
|  | 453 | u16 format; | 
| Kulikov Vasiliy | 315e8f7 | 2010-07-15 22:48:19 +0400 | [diff] [blame] | 454 | int width; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 455 | unsigned int bytes_per_sec; | 
|  | 456 |  | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 457 | print_hwparams(substream, params); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 458 | err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | 
|  | 459 | if (err < 0) | 
|  | 460 | return err; | 
|  | 461 | err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format); | 
|  | 462 | if (err) | 
|  | 463 | return err; | 
|  | 464 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 465 | hpi_handle_error(hpi_format_create(&dpcm->format, | 
|  | 466 | params_channels(params), | 
|  | 467 | format, params_rate(params), 0, 0)); | 
|  | 468 |  | 
|  | 469 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 470 | if (hpi_instream_reset(dpcm->h_stream) != 0) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 471 | return -EINVAL; | 
|  | 472 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 473 | if (hpi_instream_set_format( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 474 | dpcm->h_stream, &dpcm->format) != 0) | 
|  | 475 | return -EINVAL; | 
|  | 476 | } | 
|  | 477 |  | 
|  | 478 | dpcm->hpi_buffer_attached = 0; | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 479 | if (card->can_dma) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 480 | err = hpi_stream_host_buffer_attach(dpcm->h_stream, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 481 | params_buffer_bytes(params),  runtime->dma_addr); | 
|  | 482 | if (err == 0) { | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 483 | snd_printdd( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 484 | "stream_host_buffer_attach succeeded %u %lu\n", | 
|  | 485 | params_buffer_bytes(params), | 
|  | 486 | (unsigned long)runtime->dma_addr); | 
|  | 487 | } else { | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 488 | snd_printd("stream_host_buffer_attach error %d\n", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 489 | err); | 
|  | 490 | return -ENOMEM; | 
|  | 491 | } | 
|  | 492 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 493 | err = hpi_stream_get_info_ex(dpcm->h_stream, NULL, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 494 | &dpcm->hpi_buffer_attached, | 
|  | 495 | NULL, NULL, NULL); | 
|  | 496 |  | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 497 | snd_printdd("stream_host_buffer_attach status 0x%x\n", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 498 | dpcm->hpi_buffer_attached); | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 499 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 500 | } | 
|  | 501 | bytes_per_sec = params_rate(params) * params_channels(params); | 
| Kulikov Vasiliy | 315e8f7 | 2010-07-15 22:48:19 +0400 | [diff] [blame] | 502 | width = snd_pcm_format_width(params_format(params)); | 
|  | 503 | bytes_per_sec *= width; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 504 | bytes_per_sec /= 8; | 
| Kulikov Vasiliy | 315e8f7 | 2010-07-15 22:48:19 +0400 | [diff] [blame] | 505 | if (width < 0 || bytes_per_sec == 0) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 506 | return -EINVAL; | 
|  | 507 |  | 
|  | 508 | dpcm->bytes_per_sec = bytes_per_sec; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 509 | dpcm->buffer_bytes = params_buffer_bytes(params); | 
|  | 510 | dpcm->period_bytes = params_period_bytes(params); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 511 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 512 | return 0; | 
|  | 513 | } | 
|  | 514 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 515 | static int | 
|  | 516 | snd_card_asihpi_hw_free(struct snd_pcm_substream *substream) | 
|  | 517 | { | 
|  | 518 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 519 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 520 | if (dpcm->hpi_buffer_attached) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 521 | hpi_stream_host_buffer_detach(dpcm->h_stream); | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 522 |  | 
|  | 523 | snd_pcm_lib_free_pages(substream); | 
|  | 524 | return 0; | 
|  | 525 | } | 
|  | 526 |  | 
|  | 527 | static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime) | 
|  | 528 | { | 
|  | 529 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 530 | kfree(dpcm); | 
|  | 531 | } | 
|  | 532 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 533 | static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream * | 
|  | 534 | substream) | 
|  | 535 | { | 
|  | 536 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 537 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 538 | int expiry; | 
|  | 539 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 540 | expiry = HZ / 200; | 
|  | 541 | /*? (dpcm->period_bytes * HZ / dpcm->bytes_per_sec); */ | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 542 | expiry = max(expiry, 1); /* don't let it be zero! */ | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 543 | dpcm->timer.expires = jiffies + expiry; | 
|  | 544 | dpcm->respawn_timer = 1; | 
|  | 545 | add_timer(&dpcm->timer); | 
|  | 546 | } | 
|  | 547 |  | 
|  | 548 | static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream) | 
|  | 549 | { | 
|  | 550 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 551 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 552 |  | 
|  | 553 | dpcm->respawn_timer = 0; | 
|  | 554 | del_timer(&dpcm->timer); | 
|  | 555 | } | 
|  | 556 |  | 
|  | 557 | static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, | 
|  | 558 | int cmd) | 
|  | 559 | { | 
|  | 560 | struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data; | 
|  | 561 | struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); | 
|  | 562 | struct snd_pcm_substream *s; | 
|  | 563 | u16 e; | 
| Eliot Blennerhassett | 0a17e99 | 2011-07-22 15:52:44 +1200 | [diff] [blame] | 564 | char name[16]; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 565 |  | 
| Eliot Blennerhassett | 0a17e99 | 2011-07-22 15:52:44 +1200 | [diff] [blame] | 566 | snd_pcm_debug_name(substream, name, sizeof(name)); | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 567 | snd_printdd("%s trigger\n", name); | 
|  | 568 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 569 | switch (cmd) { | 
|  | 570 | case SNDRV_PCM_TRIGGER_START: | 
|  | 571 | snd_pcm_group_for_each_entry(s, substream) { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 572 | struct snd_pcm_runtime *runtime = s->runtime; | 
|  | 573 | struct snd_card_asihpi_pcm *ds = runtime->private_data; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 574 |  | 
|  | 575 | if (snd_pcm_substream_chip(s) != card) | 
|  | 576 | continue; | 
|  | 577 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 578 | /* don't link Cap and Play */ | 
|  | 579 | if (substream->stream != s->stream) | 
|  | 580 | continue; | 
|  | 581 |  | 
| Eliot Blennerhassett | 0b7ce9e | 2011-04-05 20:55:43 +1200 | [diff] [blame] | 582 | ds->drained_count = 0; | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 583 | if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 584 | /* How do I know how much valid data is present | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 585 | * in buffer? Must be at least one period! | 
|  | 586 | * Guessing 2 periods, but if | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 587 | * buffer is bigger it may contain even more | 
|  | 588 | * data?? | 
|  | 589 | */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 590 | unsigned int preload = ds->period_bytes * 1; | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 591 | snd_printddd("%d preload x%x\n", s->number, preload); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 592 | hpi_handle_error(hpi_outstream_write_buf( | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 593 | ds->h_stream, | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 594 | &runtime->dma_area[0], | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 595 | preload, | 
|  | 596 | &ds->format)); | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 597 | ds->pcm_buf_host_rw_ofs = preload; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 598 | } | 
|  | 599 |  | 
|  | 600 | if (card->support_grouping) { | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 601 | snd_printdd("%d group\n", s->number); | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 602 | e = hpi_stream_group_add( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 603 | dpcm->h_stream, | 
|  | 604 | ds->h_stream); | 
|  | 605 | if (!e) { | 
|  | 606 | snd_pcm_trigger_done(s, substream); | 
|  | 607 | } else { | 
|  | 608 | hpi_handle_error(e); | 
|  | 609 | break; | 
|  | 610 | } | 
|  | 611 | } else | 
|  | 612 | break; | 
|  | 613 | } | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 614 | snd_printdd("start\n"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 615 | /* start the master stream */ | 
|  | 616 | snd_card_asihpi_pcm_timer_start(substream); | 
| Eliot Blennerhassett | c4ed97d | 2011-02-10 17:26:20 +1300 | [diff] [blame] | 617 | if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) || | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 618 | !card->can_dma) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 619 | hpi_handle_error(hpi_stream_start(dpcm->h_stream)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 620 | break; | 
|  | 621 |  | 
|  | 622 | case SNDRV_PCM_TRIGGER_STOP: | 
|  | 623 | snd_card_asihpi_pcm_timer_stop(substream); | 
|  | 624 | snd_pcm_group_for_each_entry(s, substream) { | 
|  | 625 | if (snd_pcm_substream_chip(s) != card) | 
|  | 626 | continue; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 627 | /* don't link Cap and Play */ | 
|  | 628 | if (substream->stream != s->stream) | 
|  | 629 | continue; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 630 |  | 
|  | 631 | /*? workaround linked streams don't | 
|  | 632 | transition to SETUP 20070706*/ | 
|  | 633 | s->runtime->status->state = SNDRV_PCM_STATE_SETUP; | 
|  | 634 |  | 
|  | 635 | if (card->support_grouping) { | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 636 | snd_printdd("%d group\n", s->number); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 637 | snd_pcm_trigger_done(s, substream); | 
|  | 638 | } else | 
|  | 639 | break; | 
|  | 640 | } | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 641 | snd_printdd("stop\n"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 642 |  | 
|  | 643 | /* _prepare and _hwparams reset the stream */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 644 | hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 645 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
|  | 646 | hpi_handle_error( | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 647 | hpi_outstream_reset(dpcm->h_stream)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 648 |  | 
|  | 649 | if (card->support_grouping) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 650 | hpi_handle_error(hpi_stream_group_reset(dpcm->h_stream)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 651 | break; | 
|  | 652 |  | 
|  | 653 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 654 | snd_printdd("pause release\n"); | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 655 | hpi_handle_error(hpi_stream_start(dpcm->h_stream)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 656 | snd_card_asihpi_pcm_timer_start(substream); | 
|  | 657 | break; | 
|  | 658 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 659 | snd_printdd("pause\n"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 660 | snd_card_asihpi_pcm_timer_stop(substream); | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 661 | hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 662 | break; | 
|  | 663 | default: | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 664 | snd_printd(KERN_ERR "\tINVALID\n"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 665 | return -EINVAL; | 
|  | 666 | } | 
|  | 667 |  | 
|  | 668 | return 0; | 
|  | 669 | } | 
|  | 670 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 671 | /*algorithm outline | 
|  | 672 | Without linking degenerates to getting single stream pos etc | 
|  | 673 | Without mmap 2nd loop degenerates to snd_pcm_period_elapsed | 
|  | 674 | */ | 
|  | 675 | /* | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 676 | pcm_buf_dma_ofs=get_buf_pos(s); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 677 | for_each_linked_stream(s) { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 678 | pcm_buf_dma_ofs=get_buf_pos(s); | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 679 | min_buf_pos = modulo_min(min_buf_pos, pcm_buf_dma_ofs, buffer_bytes) | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 680 | new_data = min(new_data, calc_new_data(pcm_buf_dma_ofs,irq_pos) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 681 | } | 
|  | 682 | timer.expires = jiffies + predict_next_period_ready(min_buf_pos); | 
|  | 683 | for_each_linked_stream(s) { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 684 | s->pcm_buf_dma_ofs = min_buf_pos; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 685 | if (new_data > period_bytes) { | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 686 | if (mmap) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 687 | irq_pos = (irq_pos + period_bytes) % buffer_bytes; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 688 | if (playback) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 689 | write(period_bytes); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 690 | } else { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 691 | read(period_bytes); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 692 | } | 
|  | 693 | } | 
|  | 694 | snd_pcm_period_elapsed(s); | 
|  | 695 | } | 
|  | 696 | } | 
|  | 697 | */ | 
|  | 698 |  | 
|  | 699 | /** Minimum of 2 modulo values.  Works correctly when the difference between | 
|  | 700 | * the values is less than half the modulus | 
|  | 701 | */ | 
|  | 702 | static inline unsigned int modulo_min(unsigned int a, unsigned int b, | 
|  | 703 | unsigned long int modulus) | 
|  | 704 | { | 
|  | 705 | unsigned int result; | 
|  | 706 | if (((a-b) % modulus) < (modulus/2)) | 
|  | 707 | result = b; | 
|  | 708 | else | 
|  | 709 | result = a; | 
|  | 710 |  | 
|  | 711 | return result; | 
|  | 712 | } | 
|  | 713 |  | 
|  | 714 | /** Timer function, equivalent to interrupt service routine for cards | 
|  | 715 | */ | 
|  | 716 | static void snd_card_asihpi_timer_function(unsigned long data) | 
|  | 717 | { | 
|  | 718 | struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 719 | struct snd_pcm_substream *substream = dpcm->substream; | 
|  | 720 | struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 721 | struct snd_pcm_runtime *runtime; | 
|  | 722 | struct snd_pcm_substream *s; | 
|  | 723 | unsigned int newdata = 0; | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 724 | unsigned int pcm_buf_dma_ofs, min_buf_pos = 0; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 725 | unsigned int remdata, xfercount, next_jiffies; | 
|  | 726 | int first = 1; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 727 | int loops = 0; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 728 | u16 state; | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 729 | u32 buffer_size, bytes_avail, samples_played, on_card_bytes; | 
| Eliot Blennerhassett | 0a17e99 | 2011-07-22 15:52:44 +1200 | [diff] [blame] | 730 | char name[16]; | 
|  | 731 |  | 
|  | 732 | snd_pcm_debug_name(substream, name, sizeof(name)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 733 |  | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 734 | snd_printdd("%s snd_card_asihpi_timer_function\n", name); | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 735 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 736 | /* find minimum newdata and buffer pos in group */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 737 | snd_pcm_group_for_each_entry(s, substream) { | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 738 | struct snd_card_asihpi_pcm *ds = s->runtime->private_data; | 
|  | 739 | runtime = s->runtime; | 
|  | 740 |  | 
|  | 741 | if (snd_pcm_substream_chip(s) != card) | 
|  | 742 | continue; | 
|  | 743 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 744 | /* don't link Cap and Play */ | 
|  | 745 | if (substream->stream != s->stream) | 
|  | 746 | continue; | 
|  | 747 |  | 
|  | 748 | hpi_handle_error(hpi_stream_get_info_ex( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 749 | ds->h_stream, &state, | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 750 | &buffer_size, &bytes_avail, | 
|  | 751 | &samples_played, &on_card_bytes)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 752 |  | 
|  | 753 | /* number of bytes in on-card buffer */ | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 754 | runtime->delay = on_card_bytes; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 755 |  | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 756 | if (!card->can_dma) | 
|  | 757 | on_card_bytes = bytes_avail; | 
|  | 758 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 759 | if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 760 | pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 761 | if (state == HPI_STATE_STOPPED) { | 
| Eliot Blennerhassett | 0be55c4 | 2011-12-22 13:38:37 +1300 | [diff] [blame] | 762 | if (bytes_avail == 0) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 763 | hpi_handle_error(hpi_stream_start(ds->h_stream)); | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 764 | snd_printdd("P%d start\n", s->number); | 
| Eliot Blennerhassett | 0b7ce9e | 2011-04-05 20:55:43 +1200 | [diff] [blame] | 765 | ds->drained_count = 0; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 766 | } | 
|  | 767 | } else if (state == HPI_STATE_DRAINED) { | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 768 | snd_printd(KERN_WARNING "P%d drained\n", | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 769 | s->number); | 
| Eliot Blennerhassett | 0b7ce9e | 2011-04-05 20:55:43 +1200 | [diff] [blame] | 770 | ds->drained_count++; | 
| Eliot Blennerhassett | 0be55c4 | 2011-12-22 13:38:37 +1300 | [diff] [blame] | 771 | if (ds->drained_count > 20) { | 
| Eliot Blennerhassett | 0b7ce9e | 2011-04-05 20:55:43 +1200 | [diff] [blame] | 772 | snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); | 
|  | 773 | continue; | 
|  | 774 | } | 
|  | 775 | } else { | 
|  | 776 | ds->drained_count = 0; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 777 | } | 
|  | 778 | } else | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 779 | pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 780 |  | 
|  | 781 | if (first) { | 
|  | 782 | /* can't statically init min when wrap is involved */ | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 783 | min_buf_pos = pcm_buf_dma_ofs; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 784 | newdata = (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 785 | first = 0; | 
|  | 786 | } else { | 
|  | 787 | min_buf_pos = | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 788 | modulo_min(min_buf_pos, pcm_buf_dma_ofs, UINT_MAX+1L); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 789 | newdata = min( | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 790 | (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 791 | newdata); | 
|  | 792 | } | 
|  | 793 |  | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 794 | snd_printdd("hw_ptr 0x%04lX, appl_ptr 0x%04lX\n", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 795 | (unsigned long)frames_to_bytes(runtime, | 
|  | 796 | runtime->status->hw_ptr), | 
|  | 797 | (unsigned long)frames_to_bytes(runtime, | 
|  | 798 | runtime->control->appl_ptr)); | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 799 |  | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 800 | snd_printdd("%d S=%d, " | 
|  | 801 | "rw=0x%04X, dma=0x%04X, left=0x%04X, " | 
|  | 802 | "aux=0x%04X space=0x%04X\n", | 
|  | 803 | s->number, state, | 
|  | 804 | ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, | 
|  | 805 | (int)bytes_avail, | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 806 | (int)on_card_bytes, buffer_size-bytes_avail); | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 807 | loops++; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 808 | } | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 809 | pcm_buf_dma_ofs = min_buf_pos; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 810 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 811 | remdata = newdata % dpcm->period_bytes; | 
|  | 812 | xfercount = newdata - remdata; /* a multiple of period_bytes */ | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 813 | /* come back when on_card_bytes has decreased enough to allow | 
|  | 814 | write to happen, or when data has been consumed to make another | 
|  | 815 | period | 
|  | 816 | */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 817 | if (xfercount && (on_card_bytes  > dpcm->period_bytes)) | 
|  | 818 | next_jiffies = ((on_card_bytes - dpcm->period_bytes) * HZ / dpcm->bytes_per_sec); | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 819 | else | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 820 | next_jiffies = ((dpcm->period_bytes - remdata) * HZ / dpcm->bytes_per_sec); | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 821 |  | 
|  | 822 | next_jiffies = max(next_jiffies, 1U); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 823 | dpcm->timer.expires = jiffies + next_jiffies; | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 824 | snd_printdd("jif %d buf pos 0x%04X newdata 0x%04X xfer 0x%04X\n", | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 825 | next_jiffies, pcm_buf_dma_ofs, newdata, xfercount); | 
|  | 826 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 827 | snd_pcm_group_for_each_entry(s, substream) { | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 828 | struct snd_card_asihpi_pcm *ds = s->runtime->private_data; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 829 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 830 | /* don't link Cap and Play */ | 
|  | 831 | if (substream->stream != s->stream) | 
|  | 832 | continue; | 
|  | 833 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 834 | ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs; | 
|  | 835 |  | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 836 | if (xfercount && | 
|  | 837 | /* Limit use of on card fifo for playback */ | 
|  | 838 | ((on_card_bytes <= ds->period_bytes) || | 
|  | 839 | (s->stream == SNDRV_PCM_STREAM_CAPTURE))) | 
|  | 840 |  | 
|  | 841 | { | 
|  | 842 |  | 
|  | 843 | unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes; | 
|  | 844 | unsigned int xfer1, xfer2; | 
|  | 845 | char *pd = &s->runtime->dma_area[buf_ofs]; | 
|  | 846 |  | 
| Eliot Blennerhassett | b0096a6 | 2011-04-05 20:55:46 +1200 | [diff] [blame] | 847 | if (card->can_dma) { /* buffer wrap is handled at lower level */ | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 848 | xfer1 = xfercount; | 
|  | 849 | xfer2 = 0; | 
|  | 850 | } else { | 
|  | 851 | xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs); | 
|  | 852 | xfer2 = xfercount - xfer1; | 
|  | 853 | } | 
|  | 854 |  | 
|  | 855 | if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
|  | 856 | snd_printddd("P%d write1 0x%04X 0x%04X\n", | 
|  | 857 | s->number, xfer1, buf_ofs); | 
|  | 858 | hpi_handle_error( | 
|  | 859 | hpi_outstream_write_buf( | 
|  | 860 | ds->h_stream, pd, xfer1, | 
|  | 861 | &ds->format)); | 
|  | 862 |  | 
|  | 863 | if (xfer2) { | 
|  | 864 | pd = s->runtime->dma_area; | 
|  | 865 |  | 
|  | 866 | snd_printddd("P%d write2 0x%04X 0x%04X\n", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 867 | s->number, | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 868 | xfercount - xfer1, buf_ofs); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 869 | hpi_handle_error( | 
|  | 870 | hpi_outstream_write_buf( | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 871 | ds->h_stream, pd, | 
|  | 872 | xfercount - xfer1, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 873 | &ds->format)); | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 874 | } | 
|  | 875 | } else { | 
|  | 876 | snd_printddd("C%d read1 0x%04x\n", | 
|  | 877 | s->number, xfer1); | 
|  | 878 | hpi_handle_error( | 
|  | 879 | hpi_instream_read_buf( | 
|  | 880 | ds->h_stream, | 
|  | 881 | pd, xfer1)); | 
|  | 882 | if (xfer2) { | 
|  | 883 | pd = s->runtime->dma_area; | 
|  | 884 | snd_printddd("C%d read2 0x%04x\n", | 
|  | 885 | s->number, xfer2); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 886 | hpi_handle_error( | 
|  | 887 | hpi_instream_read_buf( | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 888 | ds->h_stream, | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 889 | pd, xfer2)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 890 | } | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 891 | } | 
| Eliot Blennerhassett | 47a74a5 | 2011-12-22 11:54:02 +1300 | [diff] [blame] | 892 | ds->pcm_buf_host_rw_ofs += xfercount; | 
|  | 893 | ds->pcm_buf_elapsed_dma_ofs += xfercount; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 894 | snd_pcm_period_elapsed(s); | 
|  | 895 | } | 
|  | 896 | } | 
|  | 897 |  | 
|  | 898 | if (dpcm->respawn_timer) | 
|  | 899 | add_timer(&dpcm->timer); | 
|  | 900 | } | 
|  | 901 |  | 
|  | 902 | /***************************** PLAYBACK OPS ****************/ | 
|  | 903 | static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream, | 
|  | 904 | unsigned int cmd, void *arg) | 
|  | 905 | { | 
| Eliot Blennerhassett | cbd757d | 2011-12-22 13:38:35 +1300 | [diff] [blame] | 906 | char name[16]; | 
|  | 907 | snd_pcm_debug_name(substream, name, sizeof(name)); | 
|  | 908 | snd_printddd(KERN_INFO "%s ioctl %d\n", name, cmd); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 909 | return snd_pcm_lib_ioctl(substream, cmd, arg); | 
|  | 910 | } | 
|  | 911 |  | 
|  | 912 | static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * | 
|  | 913 | substream) | 
|  | 914 | { | 
|  | 915 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 916 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 917 |  | 
| Eliot Blennerhassett | a647713 | 2011-04-05 20:55:42 +1200 | [diff] [blame] | 918 | snd_printdd("P%d prepare\n", substream->number); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 919 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 920 | hpi_handle_error(hpi_outstream_reset(dpcm->h_stream)); | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 921 | dpcm->pcm_buf_host_rw_ofs = 0; | 
|  | 922 | dpcm->pcm_buf_dma_ofs = 0; | 
|  | 923 | dpcm->pcm_buf_elapsed_dma_ofs = 0; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 924 | return 0; | 
|  | 925 | } | 
|  | 926 |  | 
|  | 927 | static snd_pcm_uframes_t | 
|  | 928 | snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) | 
|  | 929 | { | 
|  | 930 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 931 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 932 | snd_pcm_uframes_t ptr; | 
| Eliot Blennerhassett | cbd757d | 2011-12-22 13:38:35 +1300 | [diff] [blame] | 933 | char name[16]; | 
|  | 934 | snd_pcm_debug_name(substream, name, sizeof(name)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 935 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 936 | ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs  % dpcm->buffer_bytes); | 
| Eliot Blennerhassett | cbd757d | 2011-12-22 13:38:35 +1300 | [diff] [blame] | 937 | snd_printddd("%s pointer = 0x%04lx\n", name, (unsigned long)ptr); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 938 | return ptr; | 
|  | 939 | } | 
|  | 940 |  | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 941 | static u64 snd_card_asihpi_playback_formats(struct snd_card_asihpi *asihpi, | 
|  | 942 | u32 h_stream) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 943 | { | 
|  | 944 | struct hpi_format hpi_format; | 
|  | 945 | u16 format; | 
|  | 946 | u16 err; | 
|  | 947 | u32 h_control; | 
|  | 948 | u32 sample_rate = 48000; | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 949 | u64 formats = 0; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 950 |  | 
|  | 951 | /* on cards without SRC, must query at valid rate, | 
|  | 952 | * maybe set by external sync | 
|  | 953 | */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 954 | err = hpi_mixer_get_control(asihpi->h_mixer, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 955 | HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, | 
|  | 956 | HPI_CONTROL_SAMPLECLOCK, &h_control); | 
|  | 957 |  | 
|  | 958 | if (!err) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 959 | err = hpi_sample_clock_get_sample_rate(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 960 | &sample_rate); | 
|  | 961 |  | 
|  | 962 | for (format = HPI_FORMAT_PCM8_UNSIGNED; | 
|  | 963 | format <= HPI_FORMAT_PCM24_SIGNED; format++) { | 
| Eliot Blennerhassett | c1d70dd | 2011-12-22 13:38:50 +1300 | [diff] [blame] | 964 | err = hpi_format_create(&hpi_format, asihpi->out_max_chans, | 
|  | 965 | format, sample_rate, 128000, 0); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 966 | if (!err) | 
| Eliot Blennerhassett | c1d70dd | 2011-12-22 13:38:50 +1300 | [diff] [blame] | 967 | err = hpi_outstream_query_format(h_stream, &hpi_format); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 968 | if (!err && (hpi_to_alsa_formats[format] != -1)) | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 969 | formats |= (1ULL << hpi_to_alsa_formats[format]); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 970 | } | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 971 | return formats; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 972 | } | 
|  | 973 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 974 | static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) | 
|  | 975 | { | 
|  | 976 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 977 | struct snd_card_asihpi_pcm *dpcm; | 
|  | 978 | struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 979 | struct snd_pcm_hardware snd_card_asihpi_playback; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 980 | int err; | 
|  | 981 |  | 
|  | 982 | dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | 
|  | 983 | if (dpcm == NULL) | 
|  | 984 | return -ENOMEM; | 
|  | 985 |  | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 986 | err = hpi_outstream_open(card->hpi->adapter->index, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 987 | substream->number, &dpcm->h_stream); | 
|  | 988 | hpi_handle_error(err); | 
|  | 989 | if (err) | 
|  | 990 | kfree(dpcm); | 
|  | 991 | if (err == HPI_ERROR_OBJ_ALREADY_OPEN) | 
|  | 992 | return -EBUSY; | 
|  | 993 | if (err) | 
|  | 994 | return -EIO; | 
|  | 995 |  | 
|  | 996 | /*? also check ASI5000 samplerate source | 
|  | 997 | If external, only support external rate. | 
| Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 998 | If internal and other stream playing, can't switch | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 999 | */ | 
|  | 1000 |  | 
|  | 1001 | init_timer(&dpcm->timer); | 
|  | 1002 | dpcm->timer.data = (unsigned long) dpcm; | 
|  | 1003 | dpcm->timer.function = snd_card_asihpi_timer_function; | 
|  | 1004 | dpcm->substream = substream; | 
|  | 1005 | runtime->private_data = dpcm; | 
|  | 1006 | runtime->private_free = snd_card_asihpi_runtime_free; | 
|  | 1007 |  | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 1008 | memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback)); | 
|  | 1009 | snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; | 
|  | 1010 | snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1011 | /*?snd_card_asihpi_playback.period_bytes_min = | 
|  | 1012 | card->out_max_chans * 4096; */ | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 1013 | snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; | 
|  | 1014 | snd_card_asihpi_playback.periods_min = PERIODS_MIN; | 
|  | 1015 | snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; | 
|  | 1016 | /* snd_card_asihpi_playback.fifo_size = 0; */ | 
|  | 1017 | snd_card_asihpi_playback.channels_max = card->out_max_chans; | 
|  | 1018 | snd_card_asihpi_playback.channels_min = card->out_min_chans; | 
|  | 1019 | snd_card_asihpi_playback.formats = | 
|  | 1020 | snd_card_asihpi_playback_formats(card, dpcm->h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1021 |  | 
|  | 1022 | snd_card_asihpi_pcm_samplerates(card,  &snd_card_asihpi_playback); | 
|  | 1023 |  | 
|  | 1024 | snd_card_asihpi_playback.info = SNDRV_PCM_INFO_INTERLEAVED | | 
|  | 1025 | SNDRV_PCM_INFO_DOUBLE | | 
|  | 1026 | SNDRV_PCM_INFO_BATCH | | 
|  | 1027 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 1028 | SNDRV_PCM_INFO_PAUSE | | 
|  | 1029 | SNDRV_PCM_INFO_MMAP | | 
|  | 1030 | SNDRV_PCM_INFO_MMAP_VALID; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1031 |  | 
| Eliot Blennerhassett | 09c728a | 2011-12-22 13:38:38 +1300 | [diff] [blame] | 1032 | if (card->support_grouping) { | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1033 | snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START; | 
| Eliot Blennerhassett | 09c728a | 2011-12-22 13:38:38 +1300 | [diff] [blame] | 1034 | snd_pcm_set_sync(substream); | 
|  | 1035 | } | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1036 |  | 
|  | 1037 | /* struct is copied, so can create initializer dynamically */ | 
|  | 1038 | runtime->hw = snd_card_asihpi_playback; | 
|  | 1039 |  | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 1040 | if (card->can_dma) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1041 | err = snd_pcm_hw_constraint_pow2(runtime, 0, | 
|  | 1042 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES); | 
|  | 1043 | if (err < 0) | 
|  | 1044 | return err; | 
|  | 1045 |  | 
|  | 1046 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | 
|  | 1047 | card->update_interval_frames); | 
| Eliot Blennerhassett | 26aebef | 2011-03-25 15:25:47 +1300 | [diff] [blame] | 1048 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1049 | snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1050 | card->update_interval_frames * 2, UINT_MAX); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1051 |  | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 1052 | snd_printdd("playback open\n"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1053 |  | 
|  | 1054 | return 0; | 
|  | 1055 | } | 
|  | 1056 |  | 
|  | 1057 | static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream) | 
|  | 1058 | { | 
|  | 1059 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 1060 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 1061 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1062 | hpi_handle_error(hpi_outstream_close(dpcm->h_stream)); | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 1063 | snd_printdd("playback close\n"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1064 |  | 
|  | 1065 | return 0; | 
|  | 1066 | } | 
|  | 1067 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1068 | static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = { | 
|  | 1069 | .open = snd_card_asihpi_playback_open, | 
|  | 1070 | .close = snd_card_asihpi_playback_close, | 
|  | 1071 | .ioctl = snd_card_asihpi_playback_ioctl, | 
|  | 1072 | .hw_params = snd_card_asihpi_pcm_hw_params, | 
|  | 1073 | .hw_free = snd_card_asihpi_hw_free, | 
|  | 1074 | .prepare = snd_card_asihpi_playback_prepare, | 
|  | 1075 | .trigger = snd_card_asihpi_trigger, | 
|  | 1076 | .pointer = snd_card_asihpi_playback_pointer, | 
|  | 1077 | }; | 
|  | 1078 |  | 
|  | 1079 | /***************************** CAPTURE OPS ****************/ | 
|  | 1080 | static snd_pcm_uframes_t | 
|  | 1081 | snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) | 
|  | 1082 | { | 
|  | 1083 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 1084 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 1085 |  | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 1086 | snd_printddd("capture pointer %d=%d\n", | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1087 | substream->number, dpcm->pcm_buf_dma_ofs); | 
|  | 1088 | /* NOTE Unlike playback can't use actual samples_played | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1089 | for the capture position, because those samples aren't yet in | 
|  | 1090 | the local buffer available for reading. | 
|  | 1091 | */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1092 | return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1093 | } | 
|  | 1094 |  | 
|  | 1095 | static int snd_card_asihpi_capture_ioctl(struct snd_pcm_substream *substream, | 
|  | 1096 | unsigned int cmd, void *arg) | 
|  | 1097 | { | 
|  | 1098 | return snd_pcm_lib_ioctl(substream, cmd, arg); | 
|  | 1099 | } | 
|  | 1100 |  | 
|  | 1101 | static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) | 
|  | 1102 | { | 
|  | 1103 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 1104 | struct snd_card_asihpi_pcm *dpcm = runtime->private_data; | 
|  | 1105 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1106 | hpi_handle_error(hpi_instream_reset(dpcm->h_stream)); | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1107 | dpcm->pcm_buf_host_rw_ofs = 0; | 
|  | 1108 | dpcm->pcm_buf_dma_ofs = 0; | 
|  | 1109 | dpcm->pcm_buf_elapsed_dma_ofs = 0; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1110 |  | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 1111 | snd_printdd("Capture Prepare %d\n", substream->number); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1112 | return 0; | 
|  | 1113 | } | 
|  | 1114 |  | 
|  | 1115 |  | 
|  | 1116 |  | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 1117 | static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi, | 
|  | 1118 | u32 h_stream) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1119 | { | 
|  | 1120 | struct hpi_format hpi_format; | 
|  | 1121 | u16 format; | 
|  | 1122 | u16 err; | 
|  | 1123 | u32 h_control; | 
|  | 1124 | u32 sample_rate = 48000; | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 1125 | u64 formats = 0; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1126 |  | 
|  | 1127 | /* on cards without SRC, must query at valid rate, | 
|  | 1128 | maybe set by external sync */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1129 | err = hpi_mixer_get_control(asihpi->h_mixer, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1130 | HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, | 
|  | 1131 | HPI_CONTROL_SAMPLECLOCK, &h_control); | 
|  | 1132 |  | 
|  | 1133 | if (!err) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1134 | err = hpi_sample_clock_get_sample_rate(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1135 | &sample_rate); | 
|  | 1136 |  | 
|  | 1137 | for (format = HPI_FORMAT_PCM8_UNSIGNED; | 
|  | 1138 | format <= HPI_FORMAT_PCM24_SIGNED; format++) { | 
|  | 1139 |  | 
| Eliot Blennerhassett | c1d70dd | 2011-12-22 13:38:50 +1300 | [diff] [blame] | 1140 | err = hpi_format_create(&hpi_format, asihpi->in_max_chans, | 
|  | 1141 | format, sample_rate, 128000, 0); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1142 | if (!err) | 
| Eliot Blennerhassett | c1d70dd | 2011-12-22 13:38:50 +1300 | [diff] [blame] | 1143 | err = hpi_instream_query_format(h_stream, &hpi_format); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1144 | if (!err) | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 1145 | formats |= (1ULL << hpi_to_alsa_formats[format]); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1146 | } | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 1147 | return formats; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1148 | } | 
|  | 1149 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1150 | static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) | 
|  | 1151 | { | 
|  | 1152 | struct snd_pcm_runtime *runtime = substream->runtime; | 
|  | 1153 | struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); | 
|  | 1154 | struct snd_card_asihpi_pcm *dpcm; | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 1155 | struct snd_pcm_hardware snd_card_asihpi_capture; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1156 | int err; | 
|  | 1157 |  | 
|  | 1158 | dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | 
|  | 1159 | if (dpcm == NULL) | 
|  | 1160 | return -ENOMEM; | 
|  | 1161 |  | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 1162 | snd_printdd("capture open adapter %d stream %d\n", | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 1163 | card->hpi->adapter->index, substream->number); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1164 |  | 
|  | 1165 | err = hpi_handle_error( | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 1166 | hpi_instream_open(card->hpi->adapter->index, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1167 | substream->number, &dpcm->h_stream)); | 
|  | 1168 | if (err) | 
|  | 1169 | kfree(dpcm); | 
|  | 1170 | if (err == HPI_ERROR_OBJ_ALREADY_OPEN) | 
|  | 1171 | return -EBUSY; | 
|  | 1172 | if (err) | 
|  | 1173 | return -EIO; | 
|  | 1174 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1175 | init_timer(&dpcm->timer); | 
|  | 1176 | dpcm->timer.data = (unsigned long) dpcm; | 
|  | 1177 | dpcm->timer.function = snd_card_asihpi_timer_function; | 
|  | 1178 | dpcm->substream = substream; | 
|  | 1179 | runtime->private_data = dpcm; | 
|  | 1180 | runtime->private_free = snd_card_asihpi_runtime_free; | 
|  | 1181 |  | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 1182 | memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture)); | 
|  | 1183 | snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; | 
|  | 1184 | snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN; | 
|  | 1185 | snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; | 
|  | 1186 | snd_card_asihpi_capture.periods_min = PERIODS_MIN; | 
|  | 1187 | snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; | 
|  | 1188 | /* snd_card_asihpi_capture.fifo_size = 0; */ | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1189 | snd_card_asihpi_capture.channels_max = card->in_max_chans; | 
| Eliot Blennerhassett | c382a5d | 2011-12-22 13:38:33 +1300 | [diff] [blame] | 1190 | snd_card_asihpi_capture.channels_min = card->in_min_chans; | 
| Eliot Blennerhassett | 68d5339 | 2011-12-22 13:38:51 +1300 | [diff] [blame] | 1191 | snd_card_asihpi_capture.formats = | 
|  | 1192 | snd_card_asihpi_capture_formats(card, dpcm->h_stream); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1193 | snd_card_asihpi_pcm_samplerates(card,  &snd_card_asihpi_capture); | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 1194 | snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED | | 
|  | 1195 | SNDRV_PCM_INFO_MMAP | | 
|  | 1196 | SNDRV_PCM_INFO_MMAP_VALID; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1197 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1198 | if (card->support_grouping) | 
|  | 1199 | snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START; | 
|  | 1200 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1201 | runtime->hw = snd_card_asihpi_capture; | 
|  | 1202 |  | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 1203 | if (card->can_dma) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1204 | err = snd_pcm_hw_constraint_pow2(runtime, 0, | 
|  | 1205 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES); | 
|  | 1206 | if (err < 0) | 
|  | 1207 | return err; | 
|  | 1208 |  | 
|  | 1209 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | 
|  | 1210 | card->update_interval_frames); | 
|  | 1211 | snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | 
|  | 1212 | card->update_interval_frames * 2, UINT_MAX); | 
|  | 1213 |  | 
|  | 1214 | snd_pcm_set_sync(substream); | 
|  | 1215 |  | 
|  | 1216 | return 0; | 
|  | 1217 | } | 
|  | 1218 |  | 
|  | 1219 | static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream) | 
|  | 1220 | { | 
|  | 1221 | struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data; | 
|  | 1222 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1223 | hpi_handle_error(hpi_instream_close(dpcm->h_stream)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1224 | return 0; | 
|  | 1225 | } | 
|  | 1226 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1227 | static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = { | 
|  | 1228 | .open = snd_card_asihpi_capture_open, | 
|  | 1229 | .close = snd_card_asihpi_capture_close, | 
|  | 1230 | .ioctl = snd_card_asihpi_capture_ioctl, | 
|  | 1231 | .hw_params = snd_card_asihpi_pcm_hw_params, | 
|  | 1232 | .hw_free = snd_card_asihpi_hw_free, | 
|  | 1233 | .prepare = snd_card_asihpi_capture_prepare, | 
|  | 1234 | .trigger = snd_card_asihpi_trigger, | 
|  | 1235 | .pointer = snd_card_asihpi_capture_pointer, | 
|  | 1236 | }; | 
|  | 1237 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 1238 | static int __devinit snd_card_asihpi_pcm_new( | 
|  | 1239 | struct snd_card_asihpi *asihpi, int device) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1240 | { | 
|  | 1241 | struct snd_pcm *pcm; | 
|  | 1242 | int err; | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 1243 | u16 num_instreams, num_outstreams, x16; | 
|  | 1244 | u32 x32; | 
|  | 1245 |  | 
|  | 1246 | err = hpi_adapter_get_info(asihpi->hpi->adapter->index, | 
|  | 1247 | &num_outstreams, &num_instreams, | 
|  | 1248 | &x16, &x32, &x16); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1249 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1250 | err = snd_pcm_new(asihpi->card, "Asihpi PCM", device, | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 1251 | num_outstreams,	num_instreams, &pcm); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1252 | if (err < 0) | 
|  | 1253 | return err; | 
|  | 1254 | /* pointer to ops struct is stored, dont change ops afterwards! */ | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1255 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, | 
|  | 1256 | &snd_card_asihpi_playback_mmap_ops); | 
|  | 1257 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, | 
|  | 1258 | &snd_card_asihpi_capture_mmap_ops); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1259 |  | 
|  | 1260 | pcm->private_data = asihpi; | 
|  | 1261 | pcm->info_flags = 0; | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1262 | strcpy(pcm->name, "Asihpi PCM"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1263 |  | 
|  | 1264 | /*? do we want to emulate MMAP for non-BBM cards? | 
|  | 1265 | Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */ | 
|  | 1266 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | 
|  | 1267 | snd_dma_pci_data(asihpi->pci), | 
|  | 1268 | 64*1024, BUFFER_BYTES_MAX); | 
|  | 1269 |  | 
|  | 1270 | return 0; | 
|  | 1271 | } | 
|  | 1272 |  | 
|  | 1273 | /***************************** MIXER CONTROLS ****************/ | 
|  | 1274 | struct hpi_control { | 
|  | 1275 | u32 h_control; | 
|  | 1276 | u16 control_type; | 
|  | 1277 | u16 src_node_type; | 
|  | 1278 | u16 src_node_index; | 
|  | 1279 | u16 dst_node_type; | 
|  | 1280 | u16 dst_node_index; | 
|  | 1281 | u16 band; | 
|  | 1282 | char name[44]; /* copied to snd_ctl_elem_id.name[44]; */ | 
|  | 1283 | }; | 
|  | 1284 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1285 | static const char * const asihpi_tuner_band_names[] = { | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1286 | "invalid", | 
|  | 1287 | "AM", | 
|  | 1288 | "FM mono", | 
|  | 1289 | "TV NTSC-M", | 
|  | 1290 | "FM stereo", | 
|  | 1291 | "AUX", | 
|  | 1292 | "TV PAL BG", | 
|  | 1293 | "TV PAL I", | 
|  | 1294 | "TV PAL DK", | 
|  | 1295 | "TV SECAM", | 
|  | 1296 | }; | 
|  | 1297 |  | 
|  | 1298 | compile_time_assert( | 
|  | 1299 | (ARRAY_SIZE(asihpi_tuner_band_names) == | 
|  | 1300 | (HPI_TUNER_BAND_LAST+1)), | 
|  | 1301 | assert_tuner_band_names_size); | 
|  | 1302 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1303 | static const char * const asihpi_src_names[] = { | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1304 | "no source", | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1305 | "PCM", | 
|  | 1306 | "Line", | 
|  | 1307 | "Digital", | 
|  | 1308 | "Tuner", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1309 | "RF", | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1310 | "Clock", | 
|  | 1311 | "Bitstream", | 
| Eliot Blennerhassett | c830613 | 2011-07-22 15:53:00 +1200 | [diff] [blame] | 1312 | "Mic", | 
|  | 1313 | "Net", | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1314 | "Analog", | 
|  | 1315 | "Adapter", | 
| Eliot Blennerhassett | c830613 | 2011-07-22 15:53:00 +1200 | [diff] [blame] | 1316 | "RTP", | 
| Eliot Blennerhassett | 502f271 | 2011-12-22 13:38:39 +1300 | [diff] [blame] | 1317 | "Internal" | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1318 | }; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1319 |  | 
|  | 1320 | compile_time_assert( | 
|  | 1321 | (ARRAY_SIZE(asihpi_src_names) == | 
| Eliot Blennerhassett | 168f1b0 | 2010-07-06 08:37:06 +1200 | [diff] [blame] | 1322 | (HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)), | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1323 | assert_src_names_size); | 
|  | 1324 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1325 | static const char * const asihpi_dst_names[] = { | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1326 | "no destination", | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1327 | "PCM", | 
|  | 1328 | "Line", | 
|  | 1329 | "Digital", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1330 | "RF", | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1331 | "Speaker", | 
| Eliot Blennerhassett | c830613 | 2011-07-22 15:53:00 +1200 | [diff] [blame] | 1332 | "Net", | 
|  | 1333 | "Analog", | 
|  | 1334 | "RTP", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1335 | }; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1336 |  | 
|  | 1337 | compile_time_assert( | 
|  | 1338 | (ARRAY_SIZE(asihpi_dst_names) == | 
| Eliot Blennerhassett | 168f1b0 | 2010-07-06 08:37:06 +1200 | [diff] [blame] | 1339 | (HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)), | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1340 | assert_dst_names_size); | 
|  | 1341 |  | 
|  | 1342 | static inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl, | 
|  | 1343 | struct snd_card_asihpi *asihpi) | 
|  | 1344 | { | 
|  | 1345 | int err; | 
|  | 1346 |  | 
|  | 1347 | err = snd_ctl_add(card, snd_ctl_new1(ctl, asihpi)); | 
|  | 1348 | if (err < 0) | 
|  | 1349 | return err; | 
|  | 1350 | else if (mixer_dump) | 
|  | 1351 | snd_printk(KERN_INFO "added %s(%d)\n", ctl->name, ctl->index); | 
|  | 1352 |  | 
|  | 1353 | return 0; | 
|  | 1354 | } | 
|  | 1355 |  | 
|  | 1356 | /* Convert HPI control name and location into ALSA control name */ | 
|  | 1357 | static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, | 
|  | 1358 | struct hpi_control *hpi_ctl, | 
|  | 1359 | char *name) | 
|  | 1360 | { | 
| Eliot Blennerhassett | 550ac6b | 2011-04-05 20:55:41 +1200 | [diff] [blame] | 1361 | char *dir; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1362 | memset(snd_control, 0, sizeof(*snd_control)); | 
|  | 1363 | snd_control->name = hpi_ctl->name; | 
|  | 1364 | snd_control->private_value = hpi_ctl->h_control; | 
|  | 1365 | snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; | 
|  | 1366 | snd_control->index = 0; | 
|  | 1367 |  | 
| Eliot Blennerhassett | 550ac6b | 2011-04-05 20:55:41 +1200 | [diff] [blame] | 1368 | if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE) | 
|  | 1369 | dir = ""; /* clock is neither capture nor playback */ | 
|  | 1370 | else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM) | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1371 | dir = "Capture ";  /* On or towards a PCM capture destination*/ | 
|  | 1372 | else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) && | 
|  | 1373 | (!hpi_ctl->dst_node_type)) | 
|  | 1374 | dir = "Capture "; /* On a source node that is not PCM playback */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1375 | else if (hpi_ctl->src_node_type && | 
|  | 1376 | (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) && | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1377 | (hpi_ctl->dst_node_type)) | 
|  | 1378 | dir = "Monitor Playback "; /* Between an input and an output */ | 
|  | 1379 | else | 
|  | 1380 | dir = "Playback "; /* PCM Playback source, or  output node */ | 
|  | 1381 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1382 | if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type) | 
| Eliot Blennerhassett | 550ac6b | 2011-04-05 20:55:41 +1200 | [diff] [blame] | 1383 | sprintf(hpi_ctl->name, "%s %d %s %d %s%s", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1384 | asihpi_src_names[hpi_ctl->src_node_type], | 
|  | 1385 | hpi_ctl->src_node_index, | 
|  | 1386 | asihpi_dst_names[hpi_ctl->dst_node_type], | 
|  | 1387 | hpi_ctl->dst_node_index, | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1388 | dir, name); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1389 | else if (hpi_ctl->dst_node_type) { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1390 | sprintf(hpi_ctl->name, "%s %d %s%s", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1391 | asihpi_dst_names[hpi_ctl->dst_node_type], | 
|  | 1392 | hpi_ctl->dst_node_index, | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1393 | dir, name); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1394 | } else { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1395 | sprintf(hpi_ctl->name, "%s %d %s%s", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1396 | asihpi_src_names[hpi_ctl->src_node_type], | 
|  | 1397 | hpi_ctl->src_node_index, | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1398 | dir, name); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1399 | } | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1400 | /* printk(KERN_INFO "Adding %s %d to %d ",  hpi_ctl->name, | 
|  | 1401 | hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */ | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1402 | } | 
|  | 1403 |  | 
|  | 1404 | /*------------------------------------------------------------ | 
|  | 1405 | Volume controls | 
|  | 1406 | ------------------------------------------------------------*/ | 
|  | 1407 | #define VOL_STEP_mB 1 | 
|  | 1408 | static int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol, | 
|  | 1409 | struct snd_ctl_elem_info *uinfo) | 
|  | 1410 | { | 
|  | 1411 | u32 h_control = kcontrol->private_value; | 
| Eliot Blennerhassett | d4b06d2 | 2011-12-22 13:38:34 +1300 | [diff] [blame] | 1412 | u32 count; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1413 | u16 err; | 
|  | 1414 | /* native gains are in millibels */ | 
|  | 1415 | short min_gain_mB; | 
|  | 1416 | short max_gain_mB; | 
|  | 1417 | short step_gain_mB; | 
|  | 1418 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1419 | err = hpi_volume_query_range(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1420 | &min_gain_mB, &max_gain_mB, &step_gain_mB); | 
|  | 1421 | if (err) { | 
|  | 1422 | max_gain_mB = 0; | 
|  | 1423 | min_gain_mB = -10000; | 
|  | 1424 | step_gain_mB = VOL_STEP_mB; | 
|  | 1425 | } | 
|  | 1426 |  | 
| Eliot Blennerhassett | d4b06d2 | 2011-12-22 13:38:34 +1300 | [diff] [blame] | 1427 | err = hpi_meter_query_channels(h_control, &count); | 
|  | 1428 | if (err) | 
|  | 1429 | count = HPI_MAX_CHANNELS; | 
|  | 1430 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1431 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
| Eliot Blennerhassett | d4b06d2 | 2011-12-22 13:38:34 +1300 | [diff] [blame] | 1432 | uinfo->count = count; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1433 | uinfo->value.integer.min = min_gain_mB / VOL_STEP_mB; | 
|  | 1434 | uinfo->value.integer.max = max_gain_mB / VOL_STEP_mB; | 
|  | 1435 | uinfo->value.integer.step = step_gain_mB / VOL_STEP_mB; | 
|  | 1436 | return 0; | 
|  | 1437 | } | 
|  | 1438 |  | 
|  | 1439 | static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol, | 
|  | 1440 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1441 | { | 
|  | 1442 | u32 h_control = kcontrol->private_value; | 
|  | 1443 | short an_gain_mB[HPI_MAX_CHANNELS]; | 
|  | 1444 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1445 | hpi_handle_error(hpi_volume_get_gain(h_control, an_gain_mB)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1446 | ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB; | 
|  | 1447 | ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB; | 
|  | 1448 |  | 
|  | 1449 | return 0; | 
|  | 1450 | } | 
|  | 1451 |  | 
|  | 1452 | static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol, | 
|  | 1453 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1454 | { | 
|  | 1455 | int change; | 
|  | 1456 | u32 h_control = kcontrol->private_value; | 
|  | 1457 | short an_gain_mB[HPI_MAX_CHANNELS]; | 
|  | 1458 |  | 
|  | 1459 | an_gain_mB[0] = | 
|  | 1460 | (ucontrol->value.integer.value[0]) * VOL_STEP_mB; | 
|  | 1461 | an_gain_mB[1] = | 
|  | 1462 | (ucontrol->value.integer.value[1]) * VOL_STEP_mB; | 
|  | 1463 | /*  change = asihpi->mixer_volume[addr][0] != left || | 
|  | 1464 | asihpi->mixer_volume[addr][1] != right; | 
|  | 1465 | */ | 
|  | 1466 | change = 1; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1467 | hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1468 | return change; | 
|  | 1469 | } | 
|  | 1470 |  | 
|  | 1471 | static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0); | 
|  | 1472 |  | 
| Takashi Iwai | 000477a | 2011-07-22 07:57:44 +0200 | [diff] [blame] | 1473 | #define snd_asihpi_volume_mute_info	snd_ctl_boolean_mono_info | 
| Eliot Blennerhassett | fe0aa88 | 2011-07-22 15:53:03 +1200 | [diff] [blame] | 1474 |  | 
|  | 1475 | static int snd_asihpi_volume_mute_get(struct snd_kcontrol *kcontrol, | 
|  | 1476 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1477 | { | 
|  | 1478 | u32 h_control = kcontrol->private_value; | 
|  | 1479 | u32 mute; | 
|  | 1480 |  | 
|  | 1481 | hpi_handle_error(hpi_volume_get_mute(h_control, &mute)); | 
|  | 1482 | ucontrol->value.integer.value[0] = mute ? 0 : 1; | 
|  | 1483 |  | 
|  | 1484 | return 0; | 
|  | 1485 | } | 
|  | 1486 |  | 
|  | 1487 | static int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol, | 
|  | 1488 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1489 | { | 
|  | 1490 | u32 h_control = kcontrol->private_value; | 
|  | 1491 | int change = 1; | 
|  | 1492 | /* HPI currently only supports all or none muting of multichannel volume | 
|  | 1493 | ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted | 
|  | 1494 | */ | 
|  | 1495 | int mute =  ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS; | 
|  | 1496 | hpi_handle_error(hpi_volume_set_mute(h_control, mute)); | 
|  | 1497 | return change; | 
|  | 1498 | } | 
|  | 1499 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1500 | static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi, | 
|  | 1501 | struct hpi_control *hpi_ctl) | 
|  | 1502 | { | 
|  | 1503 | struct snd_card *card = asihpi->card; | 
|  | 1504 | struct snd_kcontrol_new snd_control; | 
| Eliot Blennerhassett | fe0aa88 | 2011-07-22 15:53:03 +1200 | [diff] [blame] | 1505 | int err; | 
|  | 1506 | u32 mute; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1507 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1508 | asihpi_ctl_init(&snd_control, hpi_ctl, "Volume"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1509 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | 
|  | 1510 | SNDRV_CTL_ELEM_ACCESS_TLV_READ; | 
|  | 1511 | snd_control.info = snd_asihpi_volume_info; | 
|  | 1512 | snd_control.get = snd_asihpi_volume_get; | 
|  | 1513 | snd_control.put = snd_asihpi_volume_put; | 
|  | 1514 | snd_control.tlv.p = db_scale_100; | 
|  | 1515 |  | 
| Eliot Blennerhassett | fe0aa88 | 2011-07-22 15:53:03 +1200 | [diff] [blame] | 1516 | err = ctl_add(card, &snd_control, asihpi); | 
|  | 1517 | if (err) | 
|  | 1518 | return err; | 
|  | 1519 |  | 
|  | 1520 | if (hpi_volume_get_mute(hpi_ctl->h_control, &mute) == 0) { | 
|  | 1521 | asihpi_ctl_init(&snd_control, hpi_ctl, "Switch"); | 
|  | 1522 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | 
|  | 1523 | snd_control.info = snd_asihpi_volume_mute_info; | 
|  | 1524 | snd_control.get = snd_asihpi_volume_mute_get; | 
|  | 1525 | snd_control.put = snd_asihpi_volume_mute_put; | 
|  | 1526 | err = ctl_add(card, &snd_control, asihpi); | 
|  | 1527 | } | 
|  | 1528 | return err; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1529 | } | 
|  | 1530 |  | 
|  | 1531 | /*------------------------------------------------------------ | 
|  | 1532 | Level controls | 
|  | 1533 | ------------------------------------------------------------*/ | 
|  | 1534 | static int snd_asihpi_level_info(struct snd_kcontrol *kcontrol, | 
|  | 1535 | struct snd_ctl_elem_info *uinfo) | 
|  | 1536 | { | 
|  | 1537 | u32 h_control = kcontrol->private_value; | 
|  | 1538 | u16 err; | 
|  | 1539 | short min_gain_mB; | 
|  | 1540 | short max_gain_mB; | 
|  | 1541 | short step_gain_mB; | 
|  | 1542 |  | 
|  | 1543 | err = | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1544 | hpi_level_query_range(h_control, &min_gain_mB, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1545 | &max_gain_mB, &step_gain_mB); | 
|  | 1546 | if (err) { | 
|  | 1547 | max_gain_mB = 2400; | 
|  | 1548 | min_gain_mB = -1000; | 
|  | 1549 | step_gain_mB = 100; | 
|  | 1550 | } | 
|  | 1551 |  | 
|  | 1552 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
|  | 1553 | uinfo->count = 2; | 
|  | 1554 | uinfo->value.integer.min = min_gain_mB / HPI_UNITS_PER_dB; | 
|  | 1555 | uinfo->value.integer.max = max_gain_mB / HPI_UNITS_PER_dB; | 
|  | 1556 | uinfo->value.integer.step = step_gain_mB / HPI_UNITS_PER_dB; | 
|  | 1557 | return 0; | 
|  | 1558 | } | 
|  | 1559 |  | 
|  | 1560 | static int snd_asihpi_level_get(struct snd_kcontrol *kcontrol, | 
|  | 1561 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1562 | { | 
|  | 1563 | u32 h_control = kcontrol->private_value; | 
|  | 1564 | short an_gain_mB[HPI_MAX_CHANNELS]; | 
|  | 1565 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1566 | hpi_handle_error(hpi_level_get_gain(h_control, an_gain_mB)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1567 | ucontrol->value.integer.value[0] = | 
|  | 1568 | an_gain_mB[0] / HPI_UNITS_PER_dB; | 
|  | 1569 | ucontrol->value.integer.value[1] = | 
|  | 1570 | an_gain_mB[1] / HPI_UNITS_PER_dB; | 
|  | 1571 |  | 
|  | 1572 | return 0; | 
|  | 1573 | } | 
|  | 1574 |  | 
|  | 1575 | static int snd_asihpi_level_put(struct snd_kcontrol *kcontrol, | 
|  | 1576 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1577 | { | 
|  | 1578 | int change; | 
|  | 1579 | u32 h_control = kcontrol->private_value; | 
|  | 1580 | short an_gain_mB[HPI_MAX_CHANNELS]; | 
|  | 1581 |  | 
|  | 1582 | an_gain_mB[0] = | 
|  | 1583 | (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB; | 
|  | 1584 | an_gain_mB[1] = | 
|  | 1585 | (ucontrol->value.integer.value[1]) * HPI_UNITS_PER_dB; | 
|  | 1586 | /*  change = asihpi->mixer_level[addr][0] != left || | 
|  | 1587 | asihpi->mixer_level[addr][1] != right; | 
|  | 1588 | */ | 
|  | 1589 | change = 1; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1590 | hpi_handle_error(hpi_level_set_gain(h_control, an_gain_mB)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1591 | return change; | 
|  | 1592 | } | 
|  | 1593 |  | 
|  | 1594 | static const DECLARE_TLV_DB_SCALE(db_scale_level, -1000, 100, 0); | 
|  | 1595 |  | 
|  | 1596 | static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi, | 
|  | 1597 | struct hpi_control *hpi_ctl) | 
|  | 1598 | { | 
|  | 1599 | struct snd_card *card = asihpi->card; | 
|  | 1600 | struct snd_kcontrol_new snd_control; | 
|  | 1601 |  | 
|  | 1602 | /* can't use 'volume' cos some nodes have volume as well */ | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1603 | asihpi_ctl_init(&snd_control, hpi_ctl, "Level"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1604 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | 
|  | 1605 | SNDRV_CTL_ELEM_ACCESS_TLV_READ; | 
|  | 1606 | snd_control.info = snd_asihpi_level_info; | 
|  | 1607 | snd_control.get = snd_asihpi_level_get; | 
|  | 1608 | snd_control.put = snd_asihpi_level_put; | 
|  | 1609 | snd_control.tlv.p = db_scale_level; | 
|  | 1610 |  | 
|  | 1611 | return ctl_add(card, &snd_control, asihpi); | 
|  | 1612 | } | 
|  | 1613 |  | 
|  | 1614 | /*------------------------------------------------------------ | 
|  | 1615 | AESEBU controls | 
|  | 1616 | ------------------------------------------------------------*/ | 
|  | 1617 |  | 
|  | 1618 | /* AESEBU format */ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1619 | static const char * const asihpi_aesebu_format_names[] = { | 
|  | 1620 | "N/A", "S/PDIF", "AES/EBU" }; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1621 |  | 
|  | 1622 | static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol, | 
|  | 1623 | struct snd_ctl_elem_info *uinfo) | 
|  | 1624 | { | 
|  | 1625 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 
|  | 1626 | uinfo->count = 1; | 
|  | 1627 | uinfo->value.enumerated.items = 3; | 
|  | 1628 |  | 
|  | 1629 | if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) | 
|  | 1630 | uinfo->value.enumerated.item = | 
|  | 1631 | uinfo->value.enumerated.items - 1; | 
|  | 1632 |  | 
|  | 1633 | strcpy(uinfo->value.enumerated.name, | 
|  | 1634 | asihpi_aesebu_format_names[uinfo->value.enumerated.item]); | 
|  | 1635 |  | 
|  | 1636 | return 0; | 
|  | 1637 | } | 
|  | 1638 |  | 
|  | 1639 | static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol, | 
|  | 1640 | struct snd_ctl_elem_value *ucontrol, | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1641 | u16 (*func)(u32, u16 *)) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1642 | { | 
|  | 1643 | u32 h_control = kcontrol->private_value; | 
|  | 1644 | u16 source, err; | 
|  | 1645 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1646 | err = func(h_control, &source); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1647 |  | 
|  | 1648 | /* default to N/A */ | 
|  | 1649 | ucontrol->value.enumerated.item[0] = 0; | 
|  | 1650 | /* return success but set the control to N/A */ | 
|  | 1651 | if (err) | 
|  | 1652 | return 0; | 
|  | 1653 | if (source == HPI_AESEBU_FORMAT_SPDIF) | 
|  | 1654 | ucontrol->value.enumerated.item[0] = 1; | 
|  | 1655 | if (source == HPI_AESEBU_FORMAT_AESEBU) | 
|  | 1656 | ucontrol->value.enumerated.item[0] = 2; | 
|  | 1657 |  | 
|  | 1658 | return 0; | 
|  | 1659 | } | 
|  | 1660 |  | 
|  | 1661 | static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol, | 
|  | 1662 | struct snd_ctl_elem_value *ucontrol, | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1663 | u16 (*func)(u32, u16)) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1664 | { | 
|  | 1665 | u32 h_control = kcontrol->private_value; | 
|  | 1666 |  | 
|  | 1667 | /* default to S/PDIF */ | 
|  | 1668 | u16 source = HPI_AESEBU_FORMAT_SPDIF; | 
|  | 1669 |  | 
|  | 1670 | if (ucontrol->value.enumerated.item[0] == 1) | 
|  | 1671 | source = HPI_AESEBU_FORMAT_SPDIF; | 
|  | 1672 | if (ucontrol->value.enumerated.item[0] == 2) | 
|  | 1673 | source = HPI_AESEBU_FORMAT_AESEBU; | 
|  | 1674 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1675 | if (func(h_control, source) != 0) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1676 | return -EINVAL; | 
|  | 1677 |  | 
|  | 1678 | return 1; | 
|  | 1679 | } | 
|  | 1680 |  | 
|  | 1681 | static int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol, | 
|  | 1682 | struct snd_ctl_elem_value *ucontrol) { | 
|  | 1683 | return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1684 | hpi_aesebu_receiver_get_format); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1685 | } | 
|  | 1686 |  | 
|  | 1687 | static int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol, | 
|  | 1688 | struct snd_ctl_elem_value *ucontrol) { | 
|  | 1689 | return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1690 | hpi_aesebu_receiver_set_format); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1691 | } | 
|  | 1692 |  | 
|  | 1693 | static int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol, | 
|  | 1694 | struct snd_ctl_elem_info *uinfo) | 
|  | 1695 | { | 
|  | 1696 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
|  | 1697 | uinfo->count = 1; | 
|  | 1698 |  | 
|  | 1699 | uinfo->value.integer.min = 0; | 
|  | 1700 | uinfo->value.integer.max = 0X1F; | 
|  | 1701 | uinfo->value.integer.step = 1; | 
|  | 1702 |  | 
|  | 1703 | return 0; | 
|  | 1704 | } | 
|  | 1705 |  | 
|  | 1706 | static int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol, | 
|  | 1707 | struct snd_ctl_elem_value *ucontrol) { | 
|  | 1708 |  | 
|  | 1709 | u32 h_control = kcontrol->private_value; | 
|  | 1710 | u16 status; | 
|  | 1711 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1712 | hpi_handle_error(hpi_aesebu_receiver_get_error_status( | 
|  | 1713 | h_control, &status)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1714 | ucontrol->value.integer.value[0] = status; | 
|  | 1715 | return 0; | 
|  | 1716 | } | 
|  | 1717 |  | 
|  | 1718 | static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi, | 
|  | 1719 | struct hpi_control *hpi_ctl) | 
|  | 1720 | { | 
|  | 1721 | struct snd_card *card = asihpi->card; | 
|  | 1722 | struct snd_kcontrol_new snd_control; | 
|  | 1723 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1724 | asihpi_ctl_init(&snd_control, hpi_ctl, "Format"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1725 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | 
|  | 1726 | snd_control.info = snd_asihpi_aesebu_format_info; | 
|  | 1727 | snd_control.get = snd_asihpi_aesebu_rx_format_get; | 
|  | 1728 | snd_control.put = snd_asihpi_aesebu_rx_format_put; | 
|  | 1729 |  | 
|  | 1730 |  | 
|  | 1731 | if (ctl_add(card, &snd_control, asihpi) < 0) | 
|  | 1732 | return -EINVAL; | 
|  | 1733 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1734 | asihpi_ctl_init(&snd_control, hpi_ctl, "Status"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1735 | snd_control.access = | 
|  | 1736 | SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; | 
|  | 1737 | snd_control.info = snd_asihpi_aesebu_rxstatus_info; | 
|  | 1738 | snd_control.get = snd_asihpi_aesebu_rxstatus_get; | 
|  | 1739 |  | 
|  | 1740 | return ctl_add(card, &snd_control, asihpi); | 
|  | 1741 | } | 
|  | 1742 |  | 
|  | 1743 | static int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol, | 
|  | 1744 | struct snd_ctl_elem_value *ucontrol) { | 
|  | 1745 | return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1746 | hpi_aesebu_transmitter_get_format); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1747 | } | 
|  | 1748 |  | 
|  | 1749 | static int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol, | 
|  | 1750 | struct snd_ctl_elem_value *ucontrol) { | 
|  | 1751 | return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1752 | hpi_aesebu_transmitter_set_format); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1753 | } | 
|  | 1754 |  | 
|  | 1755 |  | 
|  | 1756 | static int __devinit snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi, | 
|  | 1757 | struct hpi_control *hpi_ctl) | 
|  | 1758 | { | 
|  | 1759 | struct snd_card *card = asihpi->card; | 
|  | 1760 | struct snd_kcontrol_new snd_control; | 
|  | 1761 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 1762 | asihpi_ctl_init(&snd_control, hpi_ctl, "Format"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1763 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | 
|  | 1764 | snd_control.info = snd_asihpi_aesebu_format_info; | 
|  | 1765 | snd_control.get = snd_asihpi_aesebu_tx_format_get; | 
|  | 1766 | snd_control.put = snd_asihpi_aesebu_tx_format_put; | 
|  | 1767 |  | 
|  | 1768 | return ctl_add(card, &snd_control, asihpi); | 
|  | 1769 | } | 
|  | 1770 |  | 
|  | 1771 | /*------------------------------------------------------------ | 
|  | 1772 | Tuner controls | 
|  | 1773 | ------------------------------------------------------------*/ | 
|  | 1774 |  | 
|  | 1775 | /* Gain */ | 
|  | 1776 |  | 
|  | 1777 | static int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol, | 
|  | 1778 | struct snd_ctl_elem_info *uinfo) | 
|  | 1779 | { | 
|  | 1780 | u32 h_control = kcontrol->private_value; | 
|  | 1781 | u16 err; | 
|  | 1782 | short idx; | 
|  | 1783 | u16 gain_range[3]; | 
|  | 1784 |  | 
|  | 1785 | for (idx = 0; idx < 3; idx++) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1786 | err = hpi_tuner_query_gain(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1787 | idx, &gain_range[idx]); | 
|  | 1788 | if (err != 0) | 
|  | 1789 | return err; | 
|  | 1790 | } | 
|  | 1791 |  | 
|  | 1792 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
|  | 1793 | uinfo->count = 1; | 
|  | 1794 | uinfo->value.integer.min = ((int)gain_range[0]) / HPI_UNITS_PER_dB; | 
|  | 1795 | uinfo->value.integer.max = ((int)gain_range[1]) / HPI_UNITS_PER_dB; | 
|  | 1796 | uinfo->value.integer.step = ((int) gain_range[2]) / HPI_UNITS_PER_dB; | 
|  | 1797 | return 0; | 
|  | 1798 | } | 
|  | 1799 |  | 
|  | 1800 | static int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol, | 
|  | 1801 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1802 | { | 
|  | 1803 | /* | 
|  | 1804 | struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); | 
|  | 1805 | */ | 
|  | 1806 | u32 h_control = kcontrol->private_value; | 
|  | 1807 | short gain; | 
|  | 1808 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1809 | hpi_handle_error(hpi_tuner_get_gain(h_control, &gain)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1810 | ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB; | 
|  | 1811 |  | 
|  | 1812 | return 0; | 
|  | 1813 | } | 
|  | 1814 |  | 
|  | 1815 | static int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol, | 
|  | 1816 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1817 | { | 
|  | 1818 | /* | 
|  | 1819 | struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); | 
|  | 1820 | */ | 
|  | 1821 | u32 h_control = kcontrol->private_value; | 
|  | 1822 | short gain; | 
|  | 1823 |  | 
|  | 1824 | gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1825 | hpi_handle_error(hpi_tuner_set_gain(h_control, gain)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1826 |  | 
|  | 1827 | return 1; | 
|  | 1828 | } | 
|  | 1829 |  | 
|  | 1830 | /* Band  */ | 
|  | 1831 |  | 
|  | 1832 | static int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol, | 
|  | 1833 | u16 *band_list, u32 len) { | 
|  | 1834 | u32 h_control = kcontrol->private_value; | 
|  | 1835 | u16 err = 0; | 
|  | 1836 | u32 i; | 
|  | 1837 |  | 
|  | 1838 | for (i = 0; i < len; i++) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1839 | err = hpi_tuner_query_band( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1840 | h_control, i, &band_list[i]); | 
|  | 1841 | if (err != 0) | 
|  | 1842 | break; | 
|  | 1843 | } | 
|  | 1844 |  | 
|  | 1845 | if (err && (err != HPI_ERROR_INVALID_OBJ_INDEX)) | 
|  | 1846 | return -EIO; | 
|  | 1847 |  | 
|  | 1848 | return i; | 
|  | 1849 | } | 
|  | 1850 |  | 
|  | 1851 | static int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol, | 
|  | 1852 | struct snd_ctl_elem_info *uinfo) | 
|  | 1853 | { | 
|  | 1854 | u16 tuner_bands[HPI_TUNER_BAND_LAST]; | 
|  | 1855 | int num_bands = 0; | 
|  | 1856 |  | 
|  | 1857 | num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, | 
|  | 1858 | HPI_TUNER_BAND_LAST); | 
|  | 1859 |  | 
|  | 1860 | if (num_bands < 0) | 
|  | 1861 | return num_bands; | 
|  | 1862 |  | 
|  | 1863 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 
|  | 1864 | uinfo->count = 1; | 
|  | 1865 | uinfo->value.enumerated.items = num_bands; | 
|  | 1866 |  | 
|  | 1867 | if (num_bands > 0) { | 
|  | 1868 | if (uinfo->value.enumerated.item >= | 
|  | 1869 | uinfo->value.enumerated.items) | 
|  | 1870 | uinfo->value.enumerated.item = | 
|  | 1871 | uinfo->value.enumerated.items - 1; | 
|  | 1872 |  | 
|  | 1873 | strcpy(uinfo->value.enumerated.name, | 
|  | 1874 | asihpi_tuner_band_names[ | 
|  | 1875 | tuner_bands[uinfo->value.enumerated.item]]); | 
|  | 1876 |  | 
|  | 1877 | } | 
|  | 1878 | return 0; | 
|  | 1879 | } | 
|  | 1880 |  | 
|  | 1881 | static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol, | 
|  | 1882 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1883 | { | 
|  | 1884 | u32 h_control = kcontrol->private_value; | 
|  | 1885 | /* | 
|  | 1886 | struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); | 
|  | 1887 | */ | 
|  | 1888 | u16 band, idx; | 
|  | 1889 | u16 tuner_bands[HPI_TUNER_BAND_LAST]; | 
|  | 1890 | u32 num_bands = 0; | 
|  | 1891 |  | 
|  | 1892 | num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, | 
|  | 1893 | HPI_TUNER_BAND_LAST); | 
|  | 1894 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1895 | hpi_handle_error(hpi_tuner_get_band(h_control, &band)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1896 |  | 
|  | 1897 | ucontrol->value.enumerated.item[0] = -1; | 
|  | 1898 | for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++) | 
|  | 1899 | if (tuner_bands[idx] == band) { | 
|  | 1900 | ucontrol->value.enumerated.item[0] = idx; | 
|  | 1901 | break; | 
|  | 1902 | } | 
|  | 1903 |  | 
|  | 1904 | return 0; | 
|  | 1905 | } | 
|  | 1906 |  | 
|  | 1907 | static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, | 
|  | 1908 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1909 | { | 
|  | 1910 | /* | 
|  | 1911 | struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); | 
|  | 1912 | */ | 
|  | 1913 | u32 h_control = kcontrol->private_value; | 
|  | 1914 | u16 band; | 
|  | 1915 | u16 tuner_bands[HPI_TUNER_BAND_LAST]; | 
|  | 1916 | u32 num_bands = 0; | 
|  | 1917 |  | 
|  | 1918 | num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, | 
|  | 1919 | HPI_TUNER_BAND_LAST); | 
|  | 1920 |  | 
|  | 1921 | band = tuner_bands[ucontrol->value.enumerated.item[0]]; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1922 | hpi_handle_error(hpi_tuner_set_band(h_control, band)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1923 |  | 
|  | 1924 | return 1; | 
|  | 1925 | } | 
|  | 1926 |  | 
|  | 1927 | /* Freq */ | 
|  | 1928 |  | 
|  | 1929 | static int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol, | 
|  | 1930 | struct snd_ctl_elem_info *uinfo) | 
|  | 1931 | { | 
|  | 1932 | u32 h_control = kcontrol->private_value; | 
|  | 1933 | u16 err; | 
|  | 1934 | u16 tuner_bands[HPI_TUNER_BAND_LAST]; | 
|  | 1935 | u16 num_bands = 0, band_iter, idx; | 
|  | 1936 | u32 freq_range[3], temp_freq_range[3]; | 
|  | 1937 |  | 
|  | 1938 | num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, | 
|  | 1939 | HPI_TUNER_BAND_LAST); | 
|  | 1940 |  | 
|  | 1941 | freq_range[0] = INT_MAX; | 
|  | 1942 | freq_range[1] = 0; | 
|  | 1943 | freq_range[2] = INT_MAX; | 
|  | 1944 |  | 
|  | 1945 | for (band_iter = 0; band_iter < num_bands; band_iter++) { | 
|  | 1946 | for (idx = 0; idx < 3; idx++) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1947 | err = hpi_tuner_query_frequency(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1948 | idx, tuner_bands[band_iter], | 
|  | 1949 | &temp_freq_range[idx]); | 
|  | 1950 | if (err != 0) | 
|  | 1951 | return err; | 
|  | 1952 | } | 
|  | 1953 |  | 
|  | 1954 | /* skip band with bogus stepping */ | 
|  | 1955 | if (temp_freq_range[2] <= 0) | 
|  | 1956 | continue; | 
|  | 1957 |  | 
|  | 1958 | if (temp_freq_range[0] < freq_range[0]) | 
|  | 1959 | freq_range[0] = temp_freq_range[0]; | 
|  | 1960 | if (temp_freq_range[1] > freq_range[1]) | 
|  | 1961 | freq_range[1] = temp_freq_range[1]; | 
|  | 1962 | if (temp_freq_range[2] < freq_range[2]) | 
|  | 1963 | freq_range[2] = temp_freq_range[2]; | 
|  | 1964 | } | 
|  | 1965 |  | 
|  | 1966 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
|  | 1967 | uinfo->count = 1; | 
|  | 1968 | uinfo->value.integer.min = ((int)freq_range[0]); | 
|  | 1969 | uinfo->value.integer.max = ((int)freq_range[1]); | 
|  | 1970 | uinfo->value.integer.step = ((int)freq_range[2]); | 
|  | 1971 | return 0; | 
|  | 1972 | } | 
|  | 1973 |  | 
|  | 1974 | static int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol, | 
|  | 1975 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1976 | { | 
|  | 1977 | u32 h_control = kcontrol->private_value; | 
|  | 1978 | u32 freq; | 
|  | 1979 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1980 | hpi_handle_error(hpi_tuner_get_frequency(h_control, &freq)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1981 | ucontrol->value.integer.value[0] = freq; | 
|  | 1982 |  | 
|  | 1983 | return 0; | 
|  | 1984 | } | 
|  | 1985 |  | 
|  | 1986 | static int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol, | 
|  | 1987 | struct snd_ctl_elem_value *ucontrol) | 
|  | 1988 | { | 
|  | 1989 | u32 h_control = kcontrol->private_value; | 
|  | 1990 | u32 freq; | 
|  | 1991 |  | 
|  | 1992 | freq = ucontrol->value.integer.value[0]; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 1993 | hpi_handle_error(hpi_tuner_set_frequency(h_control, freq)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 1994 |  | 
|  | 1995 | return 1; | 
|  | 1996 | } | 
|  | 1997 |  | 
|  | 1998 | /* Tuner control group initializer  */ | 
|  | 1999 | static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi, | 
|  | 2000 | struct hpi_control *hpi_ctl) | 
|  | 2001 | { | 
|  | 2002 | struct snd_card *card = asihpi->card; | 
|  | 2003 | struct snd_kcontrol_new snd_control; | 
|  | 2004 |  | 
|  | 2005 | snd_control.private_value = hpi_ctl->h_control; | 
|  | 2006 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | 
|  | 2007 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2008 | if (!hpi_tuner_get_gain(hpi_ctl->h_control, NULL)) { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2009 | asihpi_ctl_init(&snd_control, hpi_ctl, "Gain"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2010 | snd_control.info = snd_asihpi_tuner_gain_info; | 
|  | 2011 | snd_control.get = snd_asihpi_tuner_gain_get; | 
|  | 2012 | snd_control.put = snd_asihpi_tuner_gain_put; | 
|  | 2013 |  | 
|  | 2014 | if (ctl_add(card, &snd_control, asihpi) < 0) | 
|  | 2015 | return -EINVAL; | 
|  | 2016 | } | 
|  | 2017 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2018 | asihpi_ctl_init(&snd_control, hpi_ctl, "Band"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2019 | snd_control.info = snd_asihpi_tuner_band_info; | 
|  | 2020 | snd_control.get = snd_asihpi_tuner_band_get; | 
|  | 2021 | snd_control.put = snd_asihpi_tuner_band_put; | 
|  | 2022 |  | 
|  | 2023 | if (ctl_add(card, &snd_control, asihpi) < 0) | 
|  | 2024 | return -EINVAL; | 
|  | 2025 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2026 | asihpi_ctl_init(&snd_control, hpi_ctl, "Freq"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2027 | snd_control.info = snd_asihpi_tuner_freq_info; | 
|  | 2028 | snd_control.get = snd_asihpi_tuner_freq_get; | 
|  | 2029 | snd_control.put = snd_asihpi_tuner_freq_put; | 
|  | 2030 |  | 
|  | 2031 | return ctl_add(card, &snd_control, asihpi); | 
|  | 2032 | } | 
|  | 2033 |  | 
|  | 2034 | /*------------------------------------------------------------ | 
|  | 2035 | Meter controls | 
|  | 2036 | ------------------------------------------------------------*/ | 
|  | 2037 | static int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol, | 
|  | 2038 | struct snd_ctl_elem_info *uinfo) | 
|  | 2039 | { | 
| Eliot Blennerhassett | d4b06d2 | 2011-12-22 13:38:34 +1300 | [diff] [blame] | 2040 | u32 h_control = kcontrol->private_value; | 
|  | 2041 | u32 count; | 
|  | 2042 | u16 err; | 
|  | 2043 | err = hpi_meter_query_channels(h_control, &count); | 
|  | 2044 | if (err) | 
|  | 2045 | count = HPI_MAX_CHANNELS; | 
|  | 2046 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2047 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
| Eliot Blennerhassett | d4b06d2 | 2011-12-22 13:38:34 +1300 | [diff] [blame] | 2048 | uinfo->count = count; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2049 | uinfo->value.integer.min = 0; | 
|  | 2050 | uinfo->value.integer.max = 0x7FFFFFFF; | 
|  | 2051 | return 0; | 
|  | 2052 | } | 
|  | 2053 |  | 
|  | 2054 | /* linear values for 10dB steps */ | 
|  | 2055 | static int log2lin[] = { | 
|  | 2056 | 0x7FFFFFFF, /* 0dB */ | 
|  | 2057 | 679093956, | 
|  | 2058 | 214748365, | 
|  | 2059 | 67909396, | 
|  | 2060 | 21474837, | 
|  | 2061 | 6790940, | 
|  | 2062 | 2147484, /* -60dB */ | 
|  | 2063 | 679094, | 
|  | 2064 | 214748, /* -80 */ | 
|  | 2065 | 67909, | 
|  | 2066 | 21475, /* -100 */ | 
|  | 2067 | 6791, | 
|  | 2068 | 2147, | 
|  | 2069 | 679, | 
|  | 2070 | 214, | 
|  | 2071 | 68, | 
|  | 2072 | 21, | 
|  | 2073 | 7, | 
|  | 2074 | 2 | 
|  | 2075 | }; | 
|  | 2076 |  | 
|  | 2077 | static int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol, | 
|  | 2078 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2079 | { | 
|  | 2080 | u32 h_control = kcontrol->private_value; | 
|  | 2081 | short an_gain_mB[HPI_MAX_CHANNELS], i; | 
|  | 2082 | u16 err; | 
|  | 2083 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2084 | err = hpi_meter_get_peak(h_control, an_gain_mB); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2085 |  | 
|  | 2086 | for (i = 0; i < HPI_MAX_CHANNELS; i++) { | 
|  | 2087 | if (err) { | 
|  | 2088 | ucontrol->value.integer.value[i] = 0; | 
|  | 2089 | } else if (an_gain_mB[i] >= 0) { | 
|  | 2090 | ucontrol->value.integer.value[i] = | 
|  | 2091 | an_gain_mB[i] << 16; | 
|  | 2092 | } else { | 
|  | 2093 | /* -ve is log value in millibels < -60dB, | 
|  | 2094 | * convert to (roughly!) linear, | 
|  | 2095 | */ | 
|  | 2096 | ucontrol->value.integer.value[i] = | 
|  | 2097 | log2lin[an_gain_mB[i] / -1000]; | 
|  | 2098 | } | 
|  | 2099 | } | 
|  | 2100 | return 0; | 
|  | 2101 | } | 
|  | 2102 |  | 
|  | 2103 | static int __devinit snd_asihpi_meter_add(struct snd_card_asihpi *asihpi, | 
|  | 2104 | struct hpi_control *hpi_ctl, int subidx) | 
|  | 2105 | { | 
|  | 2106 | struct snd_card *card = asihpi->card; | 
|  | 2107 | struct snd_kcontrol_new snd_control; | 
|  | 2108 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2109 | asihpi_ctl_init(&snd_control, hpi_ctl, "Meter"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2110 | snd_control.access = | 
|  | 2111 | SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; | 
|  | 2112 | snd_control.info = snd_asihpi_meter_info; | 
|  | 2113 | snd_control.get = snd_asihpi_meter_get; | 
|  | 2114 |  | 
|  | 2115 | snd_control.index = subidx; | 
|  | 2116 |  | 
|  | 2117 | return ctl_add(card, &snd_control, asihpi); | 
|  | 2118 | } | 
|  | 2119 |  | 
|  | 2120 | /*------------------------------------------------------------ | 
|  | 2121 | Multiplexer controls | 
|  | 2122 | ------------------------------------------------------------*/ | 
|  | 2123 | static int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control) | 
|  | 2124 | { | 
|  | 2125 | u32 h_control = snd_control->private_value; | 
|  | 2126 | struct hpi_control hpi_ctl; | 
|  | 2127 | int s, err; | 
|  | 2128 | for (s = 0; s < 32; s++) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2129 | err = hpi_multiplexer_query_source(h_control, s, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2130 | &hpi_ctl. | 
|  | 2131 | src_node_type, | 
|  | 2132 | &hpi_ctl. | 
|  | 2133 | src_node_index); | 
|  | 2134 | if (err) | 
|  | 2135 | break; | 
|  | 2136 | } | 
|  | 2137 | return s; | 
|  | 2138 | } | 
|  | 2139 |  | 
|  | 2140 | static int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol, | 
|  | 2141 | struct snd_ctl_elem_info *uinfo) | 
|  | 2142 | { | 
|  | 2143 | int err; | 
|  | 2144 | u16 src_node_type, src_node_index; | 
|  | 2145 | u32 h_control = kcontrol->private_value; | 
|  | 2146 |  | 
|  | 2147 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 
|  | 2148 | uinfo->count = 1; | 
|  | 2149 | uinfo->value.enumerated.items = | 
|  | 2150 | snd_card_asihpi_mux_count_sources(kcontrol); | 
|  | 2151 |  | 
|  | 2152 | if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) | 
|  | 2153 | uinfo->value.enumerated.item = | 
|  | 2154 | uinfo->value.enumerated.items - 1; | 
|  | 2155 |  | 
|  | 2156 | err = | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2157 | hpi_multiplexer_query_source(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2158 | uinfo->value.enumerated.item, | 
|  | 2159 | &src_node_type, &src_node_index); | 
|  | 2160 |  | 
|  | 2161 | sprintf(uinfo->value.enumerated.name, "%s %d", | 
| Eliot Blennerhassett | 168f1b0 | 2010-07-06 08:37:06 +1200 | [diff] [blame] | 2162 | asihpi_src_names[src_node_type - HPI_SOURCENODE_NONE], | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2163 | src_node_index); | 
|  | 2164 | return 0; | 
|  | 2165 | } | 
|  | 2166 |  | 
|  | 2167 | static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol, | 
|  | 2168 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2169 | { | 
|  | 2170 | u32 h_control = kcontrol->private_value; | 
|  | 2171 | u16 source_type, source_index; | 
|  | 2172 | u16 src_node_type, src_node_index; | 
|  | 2173 | int s; | 
|  | 2174 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2175 | hpi_handle_error(hpi_multiplexer_get_source(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2176 | &source_type, &source_index)); | 
|  | 2177 | /* Should cache this search result! */ | 
|  | 2178 | for (s = 0; s < 256; s++) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2179 | if (hpi_multiplexer_query_source(h_control, s, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2180 | &src_node_type, &src_node_index)) | 
|  | 2181 | break; | 
|  | 2182 |  | 
|  | 2183 | if ((source_type == src_node_type) | 
|  | 2184 | && (source_index == src_node_index)) { | 
|  | 2185 | ucontrol->value.enumerated.item[0] = s; | 
|  | 2186 | return 0; | 
|  | 2187 | } | 
|  | 2188 | } | 
|  | 2189 | snd_printd(KERN_WARNING | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2190 | "Control %x failed to match mux source %hu %hu\n", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2191 | h_control, source_type, source_index); | 
|  | 2192 | ucontrol->value.enumerated.item[0] = 0; | 
|  | 2193 | return 0; | 
|  | 2194 | } | 
|  | 2195 |  | 
|  | 2196 | static int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol, | 
|  | 2197 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2198 | { | 
|  | 2199 | int change; | 
|  | 2200 | u32 h_control = kcontrol->private_value; | 
|  | 2201 | u16 source_type, source_index; | 
|  | 2202 | u16 e; | 
|  | 2203 |  | 
|  | 2204 | change = 1; | 
|  | 2205 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2206 | e = hpi_multiplexer_query_source(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2207 | ucontrol->value.enumerated.item[0], | 
|  | 2208 | &source_type, &source_index); | 
|  | 2209 | if (!e) | 
|  | 2210 | hpi_handle_error( | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2211 | hpi_multiplexer_set_source(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2212 | source_type, source_index)); | 
|  | 2213 | return change; | 
|  | 2214 | } | 
|  | 2215 |  | 
|  | 2216 |  | 
|  | 2217 | static int  __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi, | 
|  | 2218 | struct hpi_control *hpi_ctl) | 
|  | 2219 | { | 
|  | 2220 | struct snd_card *card = asihpi->card; | 
|  | 2221 | struct snd_kcontrol_new snd_control; | 
|  | 2222 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2223 | asihpi_ctl_init(&snd_control, hpi_ctl, "Route"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2224 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | 
|  | 2225 | snd_control.info = snd_asihpi_mux_info; | 
|  | 2226 | snd_control.get = snd_asihpi_mux_get; | 
|  | 2227 | snd_control.put = snd_asihpi_mux_put; | 
|  | 2228 |  | 
|  | 2229 | return ctl_add(card, &snd_control, asihpi); | 
|  | 2230 |  | 
|  | 2231 | } | 
|  | 2232 |  | 
|  | 2233 | /*------------------------------------------------------------ | 
|  | 2234 | Channel mode controls | 
|  | 2235 | ------------------------------------------------------------*/ | 
|  | 2236 | static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol, | 
|  | 2237 | struct snd_ctl_elem_info *uinfo) | 
|  | 2238 | { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2239 | static const char * const mode_names[HPI_CHANNEL_MODE_LAST + 1] = { | 
|  | 2240 | "invalid", | 
|  | 2241 | "Normal", "Swap", | 
|  | 2242 | "From Left", "From Right", | 
|  | 2243 | "To Left", "To Right" | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2244 | }; | 
|  | 2245 |  | 
|  | 2246 | u32 h_control = kcontrol->private_value; | 
|  | 2247 | u16 mode; | 
|  | 2248 | int i; | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2249 | u16 mode_map[6]; | 
|  | 2250 | int valid_modes = 0; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2251 |  | 
|  | 2252 | /* HPI channel mode values can be from 1 to 6 | 
|  | 2253 | Some adapters only support a contiguous subset | 
|  | 2254 | */ | 
|  | 2255 | for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++) | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2256 | if (!hpi_channel_mode_query_mode( | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2257 | h_control, i, &mode)) { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2258 | mode_map[valid_modes] = mode; | 
|  | 2259 | valid_modes++; | 
|  | 2260 | } | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2261 |  | 
| Takashi Iwai | 74eeb14 | 2012-01-09 18:26:05 +0100 | [diff] [blame] | 2262 | if (!valid_modes) | 
|  | 2263 | return -EINVAL; | 
|  | 2264 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2265 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 
|  | 2266 | uinfo->count = 1; | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2267 | uinfo->value.enumerated.items = valid_modes; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2268 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2269 | if (uinfo->value.enumerated.item >= valid_modes) | 
|  | 2270 | uinfo->value.enumerated.item = valid_modes - 1; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2271 |  | 
|  | 2272 | strcpy(uinfo->value.enumerated.name, | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2273 | mode_names[mode_map[uinfo->value.enumerated.item]]); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2274 |  | 
|  | 2275 | return 0; | 
|  | 2276 | } | 
|  | 2277 |  | 
|  | 2278 | static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol, | 
|  | 2279 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2280 | { | 
|  | 2281 | u32 h_control = kcontrol->private_value; | 
|  | 2282 | u16 mode; | 
|  | 2283 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2284 | if (hpi_channel_mode_get(h_control, &mode)) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2285 | mode = 1; | 
|  | 2286 |  | 
|  | 2287 | ucontrol->value.enumerated.item[0] = mode - 1; | 
|  | 2288 |  | 
|  | 2289 | return 0; | 
|  | 2290 | } | 
|  | 2291 |  | 
|  | 2292 | static int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol, | 
|  | 2293 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2294 | { | 
|  | 2295 | int change; | 
|  | 2296 | u32 h_control = kcontrol->private_value; | 
|  | 2297 |  | 
|  | 2298 | change = 1; | 
|  | 2299 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2300 | hpi_handle_error(hpi_channel_mode_set(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2301 | ucontrol->value.enumerated.item[0] + 1)); | 
|  | 2302 | return change; | 
|  | 2303 | } | 
|  | 2304 |  | 
|  | 2305 |  | 
|  | 2306 | static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, | 
|  | 2307 | struct hpi_control *hpi_ctl) | 
|  | 2308 | { | 
|  | 2309 | struct snd_card *card = asihpi->card; | 
|  | 2310 | struct snd_kcontrol_new snd_control; | 
|  | 2311 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2312 | asihpi_ctl_init(&snd_control, hpi_ctl, "Mode"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2313 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | 
|  | 2314 | snd_control.info = snd_asihpi_cmode_info; | 
|  | 2315 | snd_control.get = snd_asihpi_cmode_get; | 
|  | 2316 | snd_control.put = snd_asihpi_cmode_put; | 
|  | 2317 |  | 
|  | 2318 | return ctl_add(card, &snd_control, asihpi); | 
|  | 2319 | } | 
|  | 2320 |  | 
|  | 2321 | /*------------------------------------------------------------ | 
|  | 2322 | Sampleclock source  controls | 
|  | 2323 | ------------------------------------------------------------*/ | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2324 | static char *sampleclock_sources[MAX_CLOCKSOURCES] = { | 
|  | 2325 | "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header", | 
|  | 2326 | "SMPTE", "Digital1", "Auto", "Network", "Invalid", | 
|  | 2327 | "Prev Module", | 
|  | 2328 | "Digital2", "Digital3", "Digital4", "Digital5", | 
|  | 2329 | "Digital6", "Digital7", "Digital8"}; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2330 |  | 
|  | 2331 | static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, | 
|  | 2332 | struct snd_ctl_elem_info *uinfo) | 
|  | 2333 | { | 
|  | 2334 | struct snd_card_asihpi *asihpi = | 
|  | 2335 | (struct snd_card_asihpi *)(kcontrol->private_data); | 
|  | 2336 | struct clk_cache *clkcache = &asihpi->cc; | 
|  | 2337 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 
|  | 2338 | uinfo->count = 1; | 
|  | 2339 | uinfo->value.enumerated.items = clkcache->count; | 
|  | 2340 |  | 
|  | 2341 | if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) | 
|  | 2342 | uinfo->value.enumerated.item = | 
|  | 2343 | uinfo->value.enumerated.items - 1; | 
|  | 2344 |  | 
|  | 2345 | strcpy(uinfo->value.enumerated.name, | 
|  | 2346 | clkcache->s[uinfo->value.enumerated.item].name); | 
|  | 2347 | return 0; | 
|  | 2348 | } | 
|  | 2349 |  | 
|  | 2350 | static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol, | 
|  | 2351 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2352 | { | 
|  | 2353 | struct snd_card_asihpi *asihpi = | 
|  | 2354 | (struct snd_card_asihpi *)(kcontrol->private_data); | 
|  | 2355 | struct clk_cache *clkcache = &asihpi->cc; | 
|  | 2356 | u32 h_control = kcontrol->private_value; | 
|  | 2357 | u16 source, srcindex = 0; | 
|  | 2358 | int i; | 
|  | 2359 |  | 
|  | 2360 | ucontrol->value.enumerated.item[0] = 0; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2361 | if (hpi_sample_clock_get_source(h_control, &source)) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2362 | source = 0; | 
|  | 2363 |  | 
|  | 2364 | if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2365 | if (hpi_sample_clock_get_source_index(h_control, &srcindex)) | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2366 | srcindex = 0; | 
|  | 2367 |  | 
|  | 2368 | for (i = 0; i < clkcache->count; i++) | 
|  | 2369 | if ((clkcache->s[i].source == source) && | 
|  | 2370 | (clkcache->s[i].index == srcindex)) | 
|  | 2371 | break; | 
|  | 2372 |  | 
|  | 2373 | ucontrol->value.enumerated.item[0] = i; | 
|  | 2374 |  | 
|  | 2375 | return 0; | 
|  | 2376 | } | 
|  | 2377 |  | 
|  | 2378 | static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol, | 
|  | 2379 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2380 | { | 
|  | 2381 | struct snd_card_asihpi *asihpi = | 
|  | 2382 | (struct snd_card_asihpi *)(kcontrol->private_data); | 
|  | 2383 | struct clk_cache *clkcache = &asihpi->cc; | 
|  | 2384 | int change, item; | 
|  | 2385 | u32 h_control = kcontrol->private_value; | 
|  | 2386 |  | 
|  | 2387 | change = 1; | 
|  | 2388 | item = ucontrol->value.enumerated.item[0]; | 
|  | 2389 | if (item >= clkcache->count) | 
|  | 2390 | item = clkcache->count-1; | 
|  | 2391 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2392 | hpi_handle_error(hpi_sample_clock_set_source( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2393 | h_control, clkcache->s[item].source)); | 
|  | 2394 |  | 
|  | 2395 | if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2396 | hpi_handle_error(hpi_sample_clock_set_source_index( | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2397 | h_control, clkcache->s[item].index)); | 
|  | 2398 | return change; | 
|  | 2399 | } | 
|  | 2400 |  | 
|  | 2401 | /*------------------------------------------------------------ | 
|  | 2402 | Clkrate controls | 
|  | 2403 | ------------------------------------------------------------*/ | 
|  | 2404 | /* Need to change this to enumerated control with list of rates */ | 
|  | 2405 | static int snd_asihpi_clklocal_info(struct snd_kcontrol *kcontrol, | 
|  | 2406 | struct snd_ctl_elem_info *uinfo) | 
|  | 2407 | { | 
|  | 2408 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
|  | 2409 | uinfo->count = 1; | 
|  | 2410 | uinfo->value.integer.min = 8000; | 
|  | 2411 | uinfo->value.integer.max = 192000; | 
|  | 2412 | uinfo->value.integer.step = 100; | 
|  | 2413 |  | 
|  | 2414 | return 0; | 
|  | 2415 | } | 
|  | 2416 |  | 
|  | 2417 | static int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol, | 
|  | 2418 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2419 | { | 
|  | 2420 | u32 h_control = kcontrol->private_value; | 
|  | 2421 | u32 rate; | 
|  | 2422 | u16 e; | 
|  | 2423 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2424 | e = hpi_sample_clock_get_local_rate(h_control, &rate); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2425 | if (!e) | 
|  | 2426 | ucontrol->value.integer.value[0] = rate; | 
|  | 2427 | else | 
|  | 2428 | ucontrol->value.integer.value[0] = 0; | 
|  | 2429 | return 0; | 
|  | 2430 | } | 
|  | 2431 |  | 
|  | 2432 | static int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol, | 
|  | 2433 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2434 | { | 
|  | 2435 | int change; | 
|  | 2436 | u32 h_control = kcontrol->private_value; | 
|  | 2437 |  | 
|  | 2438 | /*  change = asihpi->mixer_clkrate[addr][0] != left || | 
|  | 2439 | asihpi->mixer_clkrate[addr][1] != right; | 
|  | 2440 | */ | 
|  | 2441 | change = 1; | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2442 | hpi_handle_error(hpi_sample_clock_set_local_rate(h_control, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2443 | ucontrol->value.integer.value[0])); | 
|  | 2444 | return change; | 
|  | 2445 | } | 
|  | 2446 |  | 
|  | 2447 | static int snd_asihpi_clkrate_info(struct snd_kcontrol *kcontrol, | 
|  | 2448 | struct snd_ctl_elem_info *uinfo) | 
|  | 2449 | { | 
|  | 2450 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
|  | 2451 | uinfo->count = 1; | 
|  | 2452 | uinfo->value.integer.min = 8000; | 
|  | 2453 | uinfo->value.integer.max = 192000; | 
|  | 2454 | uinfo->value.integer.step = 100; | 
|  | 2455 |  | 
|  | 2456 | return 0; | 
|  | 2457 | } | 
|  | 2458 |  | 
|  | 2459 | static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol, | 
|  | 2460 | struct snd_ctl_elem_value *ucontrol) | 
|  | 2461 | { | 
|  | 2462 | u32 h_control = kcontrol->private_value; | 
|  | 2463 | u32 rate; | 
|  | 2464 | u16 e; | 
|  | 2465 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2466 | e = hpi_sample_clock_get_sample_rate(h_control, &rate); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2467 | if (!e) | 
|  | 2468 | ucontrol->value.integer.value[0] = rate; | 
|  | 2469 | else | 
|  | 2470 | ucontrol->value.integer.value[0] = 0; | 
|  | 2471 | return 0; | 
|  | 2472 | } | 
|  | 2473 |  | 
|  | 2474 | static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, | 
|  | 2475 | struct hpi_control *hpi_ctl) | 
|  | 2476 | { | 
|  | 2477 | struct snd_card *card = asihpi->card; | 
|  | 2478 | struct snd_kcontrol_new snd_control; | 
|  | 2479 |  | 
|  | 2480 | struct clk_cache *clkcache = &asihpi->cc; | 
|  | 2481 | u32 hSC =  hpi_ctl->h_control; | 
|  | 2482 | int has_aes_in = 0; | 
|  | 2483 | int i, j; | 
|  | 2484 | u16 source; | 
|  | 2485 |  | 
|  | 2486 | snd_control.private_value = hpi_ctl->h_control; | 
|  | 2487 |  | 
|  | 2488 | clkcache->has_local = 0; | 
|  | 2489 |  | 
|  | 2490 | for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2491 | if  (hpi_sample_clock_query_source(hSC, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2492 | i, &source)) | 
|  | 2493 | break; | 
|  | 2494 | clkcache->s[i].source = source; | 
|  | 2495 | clkcache->s[i].index = 0; | 
|  | 2496 | clkcache->s[i].name = sampleclock_sources[source]; | 
|  | 2497 | if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) | 
|  | 2498 | has_aes_in = 1; | 
|  | 2499 | if (source == HPI_SAMPLECLOCK_SOURCE_LOCAL) | 
|  | 2500 | clkcache->has_local = 1; | 
|  | 2501 | } | 
|  | 2502 | if (has_aes_in) | 
|  | 2503 | /* already will have picked up index 0 above */ | 
|  | 2504 | for (j = 1; j < 8; j++) { | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2505 | if (hpi_sample_clock_query_source_index(hSC, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2506 | j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT, | 
|  | 2507 | &source)) | 
|  | 2508 | break; | 
|  | 2509 | clkcache->s[i].source = | 
|  | 2510 | HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT; | 
|  | 2511 | clkcache->s[i].index = j; | 
|  | 2512 | clkcache->s[i].name = sampleclock_sources[ | 
|  | 2513 | j+HPI_SAMPLECLOCK_SOURCE_LAST]; | 
|  | 2514 | i++; | 
|  | 2515 | } | 
|  | 2516 | clkcache->count = i; | 
|  | 2517 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2518 | asihpi_ctl_init(&snd_control, hpi_ctl, "Source"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2519 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; | 
|  | 2520 | snd_control.info = snd_asihpi_clksrc_info; | 
|  | 2521 | snd_control.get = snd_asihpi_clksrc_get; | 
|  | 2522 | snd_control.put = snd_asihpi_clksrc_put; | 
|  | 2523 | if (ctl_add(card, &snd_control, asihpi) < 0) | 
|  | 2524 | return -EINVAL; | 
|  | 2525 |  | 
|  | 2526 |  | 
|  | 2527 | if (clkcache->has_local) { | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2528 | asihpi_ctl_init(&snd_control, hpi_ctl, "Localrate"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2529 | snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; | 
|  | 2530 | snd_control.info = snd_asihpi_clklocal_info; | 
|  | 2531 | snd_control.get = snd_asihpi_clklocal_get; | 
|  | 2532 | snd_control.put = snd_asihpi_clklocal_put; | 
|  | 2533 |  | 
|  | 2534 |  | 
|  | 2535 | if (ctl_add(card, &snd_control, asihpi) < 0) | 
|  | 2536 | return -EINVAL; | 
|  | 2537 | } | 
|  | 2538 |  | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2539 | asihpi_ctl_init(&snd_control, hpi_ctl, "Rate"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2540 | snd_control.access = | 
|  | 2541 | SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; | 
|  | 2542 | snd_control.info = snd_asihpi_clkrate_info; | 
|  | 2543 | snd_control.get = snd_asihpi_clkrate_get; | 
|  | 2544 |  | 
|  | 2545 | return ctl_add(card, &snd_control, asihpi); | 
|  | 2546 | } | 
|  | 2547 | /*------------------------------------------------------------ | 
|  | 2548 | Mixer | 
|  | 2549 | ------------------------------------------------------------*/ | 
|  | 2550 |  | 
|  | 2551 | static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) | 
|  | 2552 | { | 
|  | 2553 | struct snd_card *card = asihpi->card; | 
|  | 2554 | unsigned int idx = 0; | 
|  | 2555 | unsigned int subindex = 0; | 
|  | 2556 | int err; | 
|  | 2557 | struct hpi_control hpi_ctl, prev_ctl; | 
|  | 2558 |  | 
|  | 2559 | if (snd_BUG_ON(!asihpi)) | 
|  | 2560 | return -EINVAL; | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2561 | strcpy(card->mixername, "Asihpi Mixer"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2562 |  | 
|  | 2563 | err = | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2564 | hpi_mixer_open(asihpi->hpi->adapter->index, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2565 | &asihpi->h_mixer); | 
|  | 2566 | hpi_handle_error(err); | 
|  | 2567 | if (err) | 
|  | 2568 | return -err; | 
|  | 2569 |  | 
| Takashi Iwai | 21896bc | 2010-06-02 12:08:37 +0200 | [diff] [blame] | 2570 | memset(&prev_ctl, 0, sizeof(prev_ctl)); | 
|  | 2571 | prev_ctl.control_type = -1; | 
|  | 2572 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2573 | for (idx = 0; idx < 2000; idx++) { | 
|  | 2574 | err = hpi_mixer_get_control_by_index( | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2575 | asihpi->h_mixer, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2576 | idx, | 
|  | 2577 | &hpi_ctl.src_node_type, | 
|  | 2578 | &hpi_ctl.src_node_index, | 
|  | 2579 | &hpi_ctl.dst_node_type, | 
|  | 2580 | &hpi_ctl.dst_node_index, | 
|  | 2581 | &hpi_ctl.control_type, | 
|  | 2582 | &hpi_ctl.h_control); | 
|  | 2583 | if (err) { | 
|  | 2584 | if (err == HPI_ERROR_CONTROL_DISABLED) { | 
|  | 2585 | if (mixer_dump) | 
|  | 2586 | snd_printk(KERN_INFO | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2587 | "Disabled HPI Control(%d)\n", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2588 | idx); | 
|  | 2589 | continue; | 
|  | 2590 | } else | 
|  | 2591 | break; | 
|  | 2592 |  | 
|  | 2593 | } | 
|  | 2594 |  | 
| Eliot Blennerhassett | 168f1b0 | 2010-07-06 08:37:06 +1200 | [diff] [blame] | 2595 | hpi_ctl.src_node_type -= HPI_SOURCENODE_NONE; | 
|  | 2596 | hpi_ctl.dst_node_type -= HPI_DESTNODE_NONE; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2597 |  | 
|  | 2598 | /* ASI50xx in SSX mode has multiple meters on the same node. | 
|  | 2599 | Use subindex to create distinct ALSA controls | 
|  | 2600 | for any duplicated controls. | 
|  | 2601 | */ | 
|  | 2602 | if ((hpi_ctl.control_type == prev_ctl.control_type) && | 
|  | 2603 | (hpi_ctl.src_node_type == prev_ctl.src_node_type) && | 
|  | 2604 | (hpi_ctl.src_node_index == prev_ctl.src_node_index) && | 
|  | 2605 | (hpi_ctl.dst_node_type == prev_ctl.dst_node_type) && | 
|  | 2606 | (hpi_ctl.dst_node_index == prev_ctl.dst_node_index)) | 
|  | 2607 | subindex++; | 
|  | 2608 | else | 
|  | 2609 | subindex = 0; | 
|  | 2610 |  | 
|  | 2611 | prev_ctl = hpi_ctl; | 
|  | 2612 |  | 
|  | 2613 | switch (hpi_ctl.control_type) { | 
|  | 2614 | case HPI_CONTROL_VOLUME: | 
|  | 2615 | err = snd_asihpi_volume_add(asihpi, &hpi_ctl); | 
|  | 2616 | break; | 
|  | 2617 | case HPI_CONTROL_LEVEL: | 
|  | 2618 | err = snd_asihpi_level_add(asihpi, &hpi_ctl); | 
|  | 2619 | break; | 
|  | 2620 | case HPI_CONTROL_MULTIPLEXER: | 
|  | 2621 | err = snd_asihpi_mux_add(asihpi, &hpi_ctl); | 
|  | 2622 | break; | 
|  | 2623 | case HPI_CONTROL_CHANNEL_MODE: | 
|  | 2624 | err = snd_asihpi_cmode_add(asihpi, &hpi_ctl); | 
|  | 2625 | break; | 
|  | 2626 | case HPI_CONTROL_METER: | 
|  | 2627 | err = snd_asihpi_meter_add(asihpi, &hpi_ctl, subindex); | 
|  | 2628 | break; | 
|  | 2629 | case HPI_CONTROL_SAMPLECLOCK: | 
|  | 2630 | err = snd_asihpi_sampleclock_add( | 
|  | 2631 | asihpi, &hpi_ctl); | 
|  | 2632 | break; | 
|  | 2633 | case HPI_CONTROL_CONNECTION:	/* ignore these */ | 
|  | 2634 | continue; | 
|  | 2635 | case HPI_CONTROL_TUNER: | 
|  | 2636 | err = snd_asihpi_tuner_add(asihpi, &hpi_ctl); | 
|  | 2637 | break; | 
|  | 2638 | case HPI_CONTROL_AESEBU_TRANSMITTER: | 
|  | 2639 | err = snd_asihpi_aesebu_tx_add(asihpi, &hpi_ctl); | 
|  | 2640 | break; | 
|  | 2641 | case HPI_CONTROL_AESEBU_RECEIVER: | 
|  | 2642 | err = snd_asihpi_aesebu_rx_add(asihpi, &hpi_ctl); | 
|  | 2643 | break; | 
|  | 2644 | case HPI_CONTROL_VOX: | 
|  | 2645 | case HPI_CONTROL_BITSTREAM: | 
|  | 2646 | case HPI_CONTROL_MICROPHONE: | 
|  | 2647 | case HPI_CONTROL_PARAMETRIC_EQ: | 
|  | 2648 | case HPI_CONTROL_COMPANDER: | 
|  | 2649 | default: | 
|  | 2650 | if (mixer_dump) | 
|  | 2651 | snd_printk(KERN_INFO | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2652 | "Untranslated HPI Control" | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2653 | "(%d) %d %d %d %d %d\n", | 
|  | 2654 | idx, | 
|  | 2655 | hpi_ctl.control_type, | 
|  | 2656 | hpi_ctl.src_node_type, | 
|  | 2657 | hpi_ctl.src_node_index, | 
|  | 2658 | hpi_ctl.dst_node_type, | 
|  | 2659 | hpi_ctl.dst_node_index); | 
|  | 2660 | continue; | 
|  | 2661 | }; | 
|  | 2662 | if (err < 0) | 
|  | 2663 | return err; | 
|  | 2664 | } | 
|  | 2665 | if (HPI_ERROR_INVALID_OBJ_INDEX != err) | 
|  | 2666 | hpi_handle_error(err); | 
|  | 2667 |  | 
|  | 2668 | snd_printk(KERN_INFO "%d mixer controls found\n", idx); | 
|  | 2669 |  | 
|  | 2670 | return 0; | 
|  | 2671 | } | 
|  | 2672 |  | 
|  | 2673 | /*------------------------------------------------------------ | 
|  | 2674 | /proc interface | 
|  | 2675 | ------------------------------------------------------------*/ | 
|  | 2676 |  | 
|  | 2677 | static void | 
|  | 2678 | snd_asihpi_proc_read(struct snd_info_entry *entry, | 
|  | 2679 | struct snd_info_buffer *buffer) | 
|  | 2680 | { | 
|  | 2681 | struct snd_card_asihpi *asihpi = entry->private_data; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2682 | u32 h_control; | 
|  | 2683 | u32 rate = 0; | 
|  | 2684 | u16 source = 0; | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2685 |  | 
|  | 2686 | u16 num_outstreams; | 
|  | 2687 | u16 num_instreams; | 
|  | 2688 | u16 version; | 
|  | 2689 | u32 serial_number; | 
|  | 2690 | u16 type; | 
|  | 2691 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2692 | int err; | 
|  | 2693 |  | 
|  | 2694 | snd_iprintf(buffer, "ASIHPI driver proc file\n"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2695 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2696 | hpi_handle_error(hpi_adapter_get_info(asihpi->hpi->adapter->index, | 
|  | 2697 | &num_outstreams, &num_instreams, | 
|  | 2698 | &version, &serial_number, &type)); | 
|  | 2699 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2700 | snd_iprintf(buffer, | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2701 | "Adapter type ASI%4X\nHardware Index %d\n" | 
|  | 2702 | "%d outstreams\n%d instreams\n", | 
|  | 2703 | type, asihpi->hpi->adapter->index, | 
|  | 2704 | num_outstreams, num_instreams); | 
|  | 2705 |  | 
|  | 2706 | snd_iprintf(buffer, | 
|  | 2707 | "Serial#%d\nHardware version %c%d\nDSP code version %03d\n", | 
|  | 2708 | serial_number, ((version >> 3) & 0xf) + 'A', version & 0x7, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2709 | ((version >> 13) * 100) + ((version >> 7) & 0x3f)); | 
|  | 2710 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2711 | err = hpi_mixer_get_control(asihpi->h_mixer, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2712 | HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, | 
|  | 2713 | HPI_CONTROL_SAMPLECLOCK, &h_control); | 
|  | 2714 |  | 
|  | 2715 | if (!err) { | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2716 | err = hpi_sample_clock_get_sample_rate(h_control, &rate); | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2717 | err += hpi_sample_clock_get_source(h_control, &source); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2718 |  | 
|  | 2719 | if (!err) | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2720 | snd_iprintf(buffer, "Sample Clock %dHz, source %s\n", | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2721 | rate, sampleclock_sources[source]); | 
|  | 2722 | } | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2723 | } | 
|  | 2724 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2725 | static void __devinit snd_asihpi_proc_init(struct snd_card_asihpi *asihpi) | 
|  | 2726 | { | 
|  | 2727 | struct snd_info_entry *entry; | 
|  | 2728 |  | 
|  | 2729 | if (!snd_card_proc_new(asihpi->card, "info", &entry)) | 
|  | 2730 | snd_info_set_text_ops(entry, asihpi, snd_asihpi_proc_read); | 
|  | 2731 | } | 
|  | 2732 |  | 
|  | 2733 | /*------------------------------------------------------------ | 
|  | 2734 | HWDEP | 
|  | 2735 | ------------------------------------------------------------*/ | 
|  | 2736 |  | 
|  | 2737 | static int snd_asihpi_hpi_open(struct snd_hwdep *hw, struct file *file) | 
|  | 2738 | { | 
|  | 2739 | if (enable_hpi_hwdep) | 
|  | 2740 | return 0; | 
|  | 2741 | else | 
|  | 2742 | return -ENODEV; | 
|  | 2743 |  | 
|  | 2744 | } | 
|  | 2745 |  | 
|  | 2746 | static int snd_asihpi_hpi_release(struct snd_hwdep *hw, struct file *file) | 
|  | 2747 | { | 
|  | 2748 | if (enable_hpi_hwdep) | 
|  | 2749 | return asihpi_hpi_release(file); | 
|  | 2750 | else | 
|  | 2751 | return -ENODEV; | 
|  | 2752 | } | 
|  | 2753 |  | 
|  | 2754 | static int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file, | 
|  | 2755 | unsigned int cmd, unsigned long arg) | 
|  | 2756 | { | 
|  | 2757 | if (enable_hpi_hwdep) | 
|  | 2758 | return asihpi_hpi_ioctl(file, cmd, arg); | 
|  | 2759 | else | 
|  | 2760 | return -ENODEV; | 
|  | 2761 | } | 
|  | 2762 |  | 
|  | 2763 |  | 
|  | 2764 | /* results in /dev/snd/hwC#D0 file for each card with index # | 
|  | 2765 | also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card' | 
|  | 2766 | */ | 
|  | 2767 | static int __devinit snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, | 
|  | 2768 | int device, struct snd_hwdep **rhwdep) | 
|  | 2769 | { | 
|  | 2770 | struct snd_hwdep *hw; | 
|  | 2771 | int err; | 
|  | 2772 |  | 
|  | 2773 | if (rhwdep) | 
|  | 2774 | *rhwdep = NULL; | 
|  | 2775 | err = snd_hwdep_new(asihpi->card, "HPI", device, &hw); | 
|  | 2776 | if (err < 0) | 
|  | 2777 | return err; | 
|  | 2778 | strcpy(hw->name, "asihpi (HPI)"); | 
|  | 2779 | hw->iface = SNDRV_HWDEP_IFACE_LAST; | 
|  | 2780 | hw->ops.open = snd_asihpi_hpi_open; | 
|  | 2781 | hw->ops.ioctl = snd_asihpi_hpi_ioctl; | 
|  | 2782 | hw->ops.release = snd_asihpi_hpi_release; | 
|  | 2783 | hw->private_data = asihpi; | 
|  | 2784 | if (rhwdep) | 
|  | 2785 | *rhwdep = hw; | 
|  | 2786 | return 0; | 
|  | 2787 | } | 
|  | 2788 |  | 
|  | 2789 | /*------------------------------------------------------------ | 
|  | 2790 | CARD | 
|  | 2791 | ------------------------------------------------------------*/ | 
|  | 2792 | static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, | 
|  | 2793 | const struct pci_device_id *pci_id) | 
|  | 2794 | { | 
|  | 2795 | int err; | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2796 | struct hpi_adapter *hpi; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2797 | struct snd_card *card; | 
|  | 2798 | struct snd_card_asihpi *asihpi; | 
|  | 2799 |  | 
|  | 2800 | u32 h_control; | 
|  | 2801 | u32 h_stream; | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2802 | u32 adapter_index; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2803 |  | 
|  | 2804 | static int dev; | 
|  | 2805 | if (dev >= SNDRV_CARDS) | 
|  | 2806 | return -ENODEV; | 
|  | 2807 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2808 | /* Should this be enable[hpi->index] ? */ | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2809 | if (!enable[dev]) { | 
|  | 2810 | dev++; | 
|  | 2811 | return -ENOENT; | 
|  | 2812 | } | 
|  | 2813 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2814 | /* Initialise low-level HPI driver */ | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2815 | err = asihpi_adapter_probe(pci_dev, pci_id); | 
|  | 2816 | if (err < 0) | 
|  | 2817 | return err; | 
|  | 2818 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2819 | hpi = pci_get_drvdata(pci_dev); | 
|  | 2820 | adapter_index = hpi->adapter->index; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2821 | /* first try to give the card the same index as its hardware index */ | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2822 | err = snd_card_create(adapter_index, | 
|  | 2823 | id[adapter_index], THIS_MODULE, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2824 | sizeof(struct snd_card_asihpi), | 
|  | 2825 | &card); | 
|  | 2826 | if (err < 0) { | 
|  | 2827 | /* if that fails, try the default index==next available */ | 
|  | 2828 | err = | 
|  | 2829 | snd_card_create(index[dev], id[dev], | 
|  | 2830 | THIS_MODULE, | 
|  | 2831 | sizeof(struct snd_card_asihpi), | 
|  | 2832 | &card); | 
|  | 2833 | if (err < 0) | 
|  | 2834 | return err; | 
|  | 2835 | snd_printk(KERN_WARNING | 
| Eliot Blennerhassett | e64b1a2 | 2011-02-10 17:25:59 +1300 | [diff] [blame] | 2836 | "**** WARNING **** Adapter index %d->ALSA index %d\n", | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2837 | adapter_index, card->number); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2838 | } | 
|  | 2839 |  | 
| Eliot Blennerhassett | 1225367 | 2011-02-10 17:26:10 +1300 | [diff] [blame] | 2840 | snd_card_set_dev(card, &pci_dev->dev); | 
|  | 2841 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2842 | asihpi = card->private_data; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2843 | asihpi->card = card; | 
| Eliot Blennerhassett | 1225367 | 2011-02-10 17:26:10 +1300 | [diff] [blame] | 2844 | asihpi->pci = pci_dev; | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2845 | asihpi->hpi = hpi; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2846 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2847 | snd_printk(KERN_INFO "adapter ID=%4X index=%d\n", | 
|  | 2848 | asihpi->hpi->adapter->type, adapter_index); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2849 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2850 | err = hpi_adapter_get_property(adapter_index, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2851 | HPI_ADAPTER_PROPERTY_CAPS1, | 
|  | 2852 | NULL, &asihpi->support_grouping); | 
|  | 2853 | if (err) | 
|  | 2854 | asihpi->support_grouping = 0; | 
|  | 2855 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2856 | err = hpi_adapter_get_property(adapter_index, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2857 | HPI_ADAPTER_PROPERTY_CAPS2, | 
|  | 2858 | &asihpi->support_mrx, NULL); | 
|  | 2859 | if (err) | 
|  | 2860 | asihpi->support_mrx = 0; | 
|  | 2861 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2862 | err = hpi_adapter_get_property(adapter_index, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2863 | HPI_ADAPTER_PROPERTY_INTERVAL, | 
|  | 2864 | NULL, &asihpi->update_interval_frames); | 
|  | 2865 | if (err) | 
|  | 2866 | asihpi->update_interval_frames = 512; | 
|  | 2867 |  | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 2868 | if (!asihpi->can_dma) | 
| Eliot Blennerhassett | 26aebef | 2011-03-25 15:25:47 +1300 | [diff] [blame] | 2869 | asihpi->update_interval_frames *= 2; | 
|  | 2870 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2871 | hpi_handle_error(hpi_instream_open(adapter_index, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2872 | 0, &h_stream)); | 
|  | 2873 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2874 | err = hpi_instream_host_buffer_free(h_stream); | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 2875 | asihpi->can_dma = (!err); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2876 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2877 | hpi_handle_error(hpi_instream_close(h_stream)); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2878 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2879 | err = hpi_adapter_get_property(adapter_index, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2880 | HPI_ADAPTER_PROPERTY_CURCHANNELS, | 
|  | 2881 | &asihpi->in_max_chans, &asihpi->out_max_chans); | 
|  | 2882 | if (err) { | 
|  | 2883 | asihpi->in_max_chans = 2; | 
|  | 2884 | asihpi->out_max_chans = 2; | 
|  | 2885 | } | 
|  | 2886 |  | 
| Eliot Blennerhassett | c382a5d | 2011-12-22 13:38:33 +1300 | [diff] [blame] | 2887 | if (asihpi->out_max_chans > 2) { /* assume LL mode */ | 
|  | 2888 | asihpi->out_min_chans = asihpi->out_max_chans; | 
|  | 2889 | asihpi->in_min_chans = asihpi->in_max_chans; | 
|  | 2890 | asihpi->support_grouping = 0; | 
|  | 2891 | } else { | 
|  | 2892 | asihpi->out_min_chans = 1; | 
|  | 2893 | asihpi->in_min_chans = 1; | 
|  | 2894 | } | 
|  | 2895 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2896 | snd_printk(KERN_INFO "Has dma:%d, grouping:%d, mrx:%d\n", | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 2897 | asihpi->can_dma, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2898 | asihpi->support_grouping, | 
|  | 2899 | asihpi->support_mrx | 
|  | 2900 | ); | 
|  | 2901 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2902 | err = snd_card_asihpi_pcm_new(asihpi, 0); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2903 | if (err < 0) { | 
|  | 2904 | snd_printk(KERN_ERR "pcm_new failed\n"); | 
|  | 2905 | goto __nodev; | 
|  | 2906 | } | 
|  | 2907 | err = snd_card_asihpi_mixer_new(asihpi); | 
|  | 2908 | if (err < 0) { | 
|  | 2909 | snd_printk(KERN_ERR "mixer_new failed\n"); | 
|  | 2910 | goto __nodev; | 
|  | 2911 | } | 
|  | 2912 |  | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2913 | err = hpi_mixer_get_control(asihpi->h_mixer, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2914 | HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, | 
|  | 2915 | HPI_CONTROL_SAMPLECLOCK, &h_control); | 
|  | 2916 |  | 
|  | 2917 | if (!err) | 
|  | 2918 | err = hpi_sample_clock_set_local_rate( | 
| Eliot Blennerhassett | ba94455 | 2011-02-10 17:26:04 +1300 | [diff] [blame] | 2919 | h_control, adapter_fs); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2920 |  | 
|  | 2921 | snd_asihpi_proc_init(asihpi); | 
|  | 2922 |  | 
|  | 2923 | /* always create, can be enabled or disabled dynamically | 
|  | 2924 | by enable_hwdep  module param*/ | 
|  | 2925 | snd_asihpi_hpi_new(asihpi, 0, NULL); | 
|  | 2926 |  | 
| Eliot Blennerhassett | f3d145a | 2011-04-05 20:55:44 +1200 | [diff] [blame] | 2927 | strcpy(card->driver, "ASIHPI"); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2928 |  | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2929 | sprintf(card->shortname, "AudioScience ASI%4X", | 
|  | 2930 | asihpi->hpi->adapter->type); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2931 | sprintf(card->longname, "%s %i", | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2932 | card->shortname, adapter_index); | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2933 | err = snd_card_register(card); | 
| Eliot Blennerhassett | b2e65c8 | 2011-03-25 15:25:48 +1300 | [diff] [blame] | 2934 |  | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2935 | if (!err) { | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2936 | hpi->snd_card = card; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2937 | dev++; | 
|  | 2938 | return 0; | 
|  | 2939 | } | 
|  | 2940 | __nodev: | 
|  | 2941 | snd_card_free(card); | 
|  | 2942 | snd_printk(KERN_ERR "snd_asihpi_probe error %d\n", err); | 
|  | 2943 | return err; | 
|  | 2944 |  | 
|  | 2945 | } | 
|  | 2946 |  | 
|  | 2947 | static void __devexit snd_asihpi_remove(struct pci_dev *pci_dev) | 
|  | 2948 | { | 
| Eliot Blennerhassett | 7036b92 | 2011-12-22 13:38:43 +1300 | [diff] [blame] | 2949 | struct hpi_adapter *hpi = pci_get_drvdata(pci_dev); | 
|  | 2950 | snd_card_free(hpi->snd_card); | 
|  | 2951 | hpi->snd_card = NULL; | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2952 | asihpi_adapter_remove(pci_dev); | 
|  | 2953 | } | 
|  | 2954 |  | 
|  | 2955 | static DEFINE_PCI_DEVICE_TABLE(asihpi_pci_tbl) = { | 
|  | 2956 | {HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205, | 
|  | 2957 | HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, | 
|  | 2958 | (kernel_ulong_t)HPI_6205}, | 
|  | 2959 | {HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040, | 
|  | 2960 | HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, | 
|  | 2961 | (kernel_ulong_t)HPI_6000}, | 
|  | 2962 | {0,} | 
|  | 2963 | }; | 
|  | 2964 | MODULE_DEVICE_TABLE(pci, asihpi_pci_tbl); | 
|  | 2965 |  | 
|  | 2966 | static struct pci_driver driver = { | 
| Takashi Iwai | 3733e42 | 2011-06-10 16:20:20 +0200 | [diff] [blame] | 2967 | .name = KBUILD_MODNAME, | 
| Eliot Blennerhassett | 719f82d | 2010-04-21 18:17:39 +0200 | [diff] [blame] | 2968 | .id_table = asihpi_pci_tbl, | 
|  | 2969 | .probe = snd_asihpi_probe, | 
|  | 2970 | .remove = __devexit_p(snd_asihpi_remove), | 
|  | 2971 | #ifdef CONFIG_PM | 
|  | 2972 | /*	.suspend = snd_asihpi_suspend, | 
|  | 2973 | .resume = snd_asihpi_resume, */ | 
|  | 2974 | #endif | 
|  | 2975 | }; | 
|  | 2976 |  | 
|  | 2977 | static int __init snd_asihpi_init(void) | 
|  | 2978 | { | 
|  | 2979 | asihpi_init(); | 
|  | 2980 | return pci_register_driver(&driver); | 
|  | 2981 | } | 
|  | 2982 |  | 
|  | 2983 | static void __exit snd_asihpi_exit(void) | 
|  | 2984 | { | 
|  | 2985 |  | 
|  | 2986 | pci_unregister_driver(&driver); | 
|  | 2987 | asihpi_exit(); | 
|  | 2988 | } | 
|  | 2989 |  | 
|  | 2990 | module_init(snd_asihpi_init) | 
|  | 2991 | module_exit(snd_asihpi_exit) | 
|  | 2992 |  |