diff options
Diffstat (limited to 'utils/mixgen2.c')
-rw-r--r-- | utils/mixgen2.c | 1233 |
1 files changed, 1233 insertions, 0 deletions
diff --git a/utils/mixgen2.c b/utils/mixgen2.c new file mode 100644 index 0000000..41a55a9 --- /dev/null +++ b/utils/mixgen2.c @@ -0,0 +1,1233 @@ +/* + * Temporary utility used to develop the next hdaudio driver + * version. Not usable. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <string.h> +#include <soundcard.h> +#include <stdarg.h> + +#define DRIVER_NICK "hdaudio" +#define CE_CONT 0 +#define CE_NOTE 1 +#define CE_WARN 2 + +#define DDB(x) + +#define PMALLOC(osdev, s) malloc(s) + +typedef int sound_os_info, mixer_create_controls_t; + +typedef struct +{ + char *hw_info; +} oss_device_t; + +#include <hdaudio.h> + +#define ioctl_arg int +#define mixer_ext_init_fn int +#include <hdaudio_codec.h> +//#include <hdaudio_codecids.h> + +int fd; + +#undef corb_read +#undef corb_write + +typedef struct +{ + int association, sequence; + int nwidgets; + int jack_count; + +#define PATH_MAXWID 8 + widget_t *widgets[PATH_MAXWID]; +} path_t; + +#define MAX_PATHS 32 +path_t *paths[MAX_PATHS]; +int npaths=0; + +int +corb_write (void *dc, unsigned int cad, unsigned int nid, unsigned int d, + unsigned int verb, unsigned int parm) +{ + unsigned int tmp; + + tmp = (cad << 28) | (d << 27) | (nid << 20) | (verb << 8) | parm; + + if (ioctl (fd, HDA_IOCTL_WRITE, &tmp) == -1) + { + perror ("HDA_IOCTL_WRITE"); + return 0; + } + + return 1; +} + +static int +corb_read (void *dc, unsigned int cad, unsigned int nid, unsigned int d, + unsigned int verb, unsigned int parm, unsigned int *upper, + unsigned int *lower) +{ + unsigned int tmp; + + tmp = (cad << 28) | (d << 27) | (nid << 20) | (verb << 8) | parm; + + if (ioctl (fd, HDA_IOCTL_READ, &tmp) == -1) + { + /* perror("HDA_IOCTL_READ"); */ + if (errno == EINVAL) + { + fprintf (stderr, "hdaudio_snoopy mode is not available\n"); + exit (-1); + } + return 0; + } + + *upper = tmp; + *lower = 0; + + return 1; +} + +void +cmn_err (int level, char *s, ...) +{ + char tmp[1024], *a[6]; + va_list ap; + int i, n = 0; + + va_start (ap, s); + + for (i = 0; i < strlen (s); i++) + if (s[i] == '%') + n++; + + for (i = 0; i < n && i < 6; i++) + { + a[i] = va_arg (ap, char *); + } + + for (i = n; i < 6; i++) + a[i] = NULL; + + if (level == CE_CONT) + { + sprintf (tmp, s, a[0], a[1], a[2], a[3], a[4], a[5], NULL, + NULL, NULL, NULL); + printf ("%s", tmp); + } + else + { + strcpy (tmp, DRIVER_NICK ": "); + + sprintf (tmp + strlen (tmp), s, a[0], a[1], a[2], a[3], a[4], a[5], + NULL, NULL, NULL, NULL); + printf ("%s", tmp); + } + + va_end (ap); +} + +/* + ******************************** + */ + +static const char *widget_id[16] = { + "pcm", + "rec", + "mix", + "select", + "jack", + "power", + "vol", + "beep", + "unkn", + "unkn", + "unkn", + "unkn", + "unkn", + "unkn", + "unkn", + "vendor" + }; + +static int +attach_pin_widget (hdaudio_mixer_t * mixer, codec_t * codec, + widget_t * widget, int cad, int wid, int group_type) +{ + unsigned int conf; + int num = codec->jack_number++; + unsigned int pincaps, b; + int association, sequence; + int default_device; + int default_loc; + int conn; + + int color; + int no_color = 0; + char *name = NULL, *loc = "", *color_name = NULL; + + if (!corb_read (mixer, cad, wid, 0, GET_CONFIG_DEFAULT, 0, &conf, &b)) + return 0; + + default_device = (conf >> 20) & 0x0f; + default_loc = (conf >> 24) & 0x3f; + conn = (conf >> 30) & 0x03; + + if (!corb_read (mixer, cad, wid, 0, + GET_PARAMETER, HDA_PIN_CAPS, &pincaps, &b)) + { + cmn_err (CE_WARN, "GET_PARAMETER HDA_PIN_CAPS failed\n"); + return 0; + } + + widget->pincaps = pincaps; + + if (conf == 0) + { + cmn_err (CE_WARN, + "CONFIG_DEFAULT information not provided by BIOS for cad=%d, wid=%02x\n", + cad, wid); + cmn_err (CE_CONT, "Cannot work in this system.\n"); + return 0; + } + + color = (conf >> 12) & 0x0f; + + association = (conf>>4) & 0xf; + sequence = conf & 0xf; + + if (pincaps & (1 << 6)) + cmn_err (CE_WARN, "Balanced I/O not supported\n"); + if (!(pincaps & (1 << 5))) /* No input */ + widget->pin_type = PIN_OUT; + if (!(pincaps & (1 << 4))) + widget->pin_type = PIN_IN; + + DDB (cmn_err (CE_CONT, "\tConfig default %08x\n", conf)); + + if ((default_loc & 0x0f) == 0x1) /* Rear panel - default */ + loc = ""; + if ((default_loc & 0x0f) == 0x2) /* Front panel */ + loc = "fp-"; + if ((default_loc & 0xf0) == 0x10) /* Internal func - eg cd/tad/spk */ + loc = "int-"; + + if (conn == 1) /* Pin not connected to anything */ + { + widget->skip = 1; + widget->skip_output = 1; + } + + widget->association = association; + widget->sequence = sequence; + + switch (default_device) + { + case 0x0: + name = "lineout"; + widget->pin_type = PIN_OUT; + break; + case 0x1: + name = "speaker"; + no_color = 1; + widget->pin_type = PIN_OUT; + break; + case 0x2: + name = "headphone"; + widget->pin_type = PIN_OUT; + break; + case 0x3: + name = "cd"; + no_color = 1; + widget->pin_type = PIN_IN; + break; + case 0x4: + name = "spdifout"; + no_color = 1; + widget->pin_type = PIN_OUT; + break; + case 0x5: + name = "digout"; + no_color = 1; + widget->pin_type = PIN_OUT; + break; + case 0x6: + name = "modem"; + no_color = 1; + break; + case 0x7: + name = "phone"; + no_color = 1; + break; + case 0x8: + name = "linein"; + widget->pin_type = PIN_IN; + break; + case 0x9: + name = "aux"; + break; + case 0xa: + name = "mic"; + widget->pin_type = PIN_MIC; + break; + case 0xb: + name = "telephony"; + no_color = 1; + break; + case 0xc: + name = "spdifin"; + no_color = 1; + break; + case 0xd: + name = "digin"; + no_color = 1; + break; + case 0xe: + name = "reserved"; + no_color = 1; + break; + case 0xf: /* Unused pin widget */ + widget->skip = 1; + widget->skip_output = 1; + break; + } + +/* process only colored jacks and skip fixed function jacks */ + switch (color) + { + case 0x1: + color_name = "black"; + widget->rgbcolor = OSS_RGB_BLACK; + break; + case 0x2: + color_name = "gray"; + widget->rgbcolor = OSS_RGB_GRAY; + break; + case 0x3: + color_name = "blue"; + widget->rgbcolor = OSS_RGB_BLUE; + break; + case 0x4: + color_name = "green"; + widget->rgbcolor = OSS_RGB_GREEN; + break; + case 0x5: + color_name = "red"; + widget->rgbcolor = OSS_RGB_RED; + break; + case 0x6: + color_name = "orange"; + widget->rgbcolor = OSS_RGB_ORANGE; + break; + case 0x7: + color_name = "yellow"; + widget->rgbcolor = OSS_RGB_YELLOW; + break; + case 0x8: + color_name = "purple"; + widget->rgbcolor = OSS_RGB_PURPLE; + break; + case 0x9: + color_name = "pink"; + widget->rgbcolor = OSS_RGB_PINK; + break; + case 0xe: + color_name = "white"; + widget->rgbcolor = OSS_RGB_WHITE; + break; + + default: + if (name != NULL) + color_name = name; + else + color_name = "internal"; + } + + if (no_color) + widget->rgbcolor = 0; + + if (default_device == 0xf) /* Not present */ + { + widget->rgbcolor = 0; + color_name = "internal"; + } + + sprintf (widget->color, "%s%s", loc, color_name); + + if (name == NULL || default_device == 0x00) + name = color_name; + sprintf (widget->name, "%s%s", loc, name); + + DDB(cmn_err(CE_CONT, "\tJack name %s, color %s (%06x)\n", + widget->name, widget->color, widget->rgbcolor)); + DDB(cmn_err(CE_CONT, "\tAssociation %x, sequence %x\n", association, sequence)); + +printf("Widget %02x: default_device=%d, name=%s, color=%s\n", wid, default_device, widget->name, widget->color); + return 1; +} + +static int +attach_widget (hdaudio_mixer_t * mixer, int cad, int wid, int group_type) +{ + static const char *widget_types[16] = { + "Audio output", + "Audio input", + "Audio mixer", + "Audio selector", + "Pin complex", + "Power widget", + "Volume knob", + "Beep generator", + "Reserved8", + "Reserved9", + "ReservedA", + "ReservedB", + "ReservedC", + "ReservedD", + "ReservedE", + "Vendor defined audio" + }; + + static const int bit_sizes[] = { + 8, + 16, + 20, + 24, + 32 + }; + + static const unsigned int bit_fmts[] = { + AFMT_U8, + AFMT_S16_LE, + AFMT_S32_LE, + AFMT_S32_LE, + AFMT_S32_LE + }; + + static const int srates[] = { + 8000, + 11025, + 16000, + 22050, + 32000, + 44100, + 48000, + 88200, + 96000, + 176400, + 192000, + 384000 + }; + + unsigned int widget_caps, b, pstate; + unsigned int inamp_caps, outamp_caps; + int wid_type; + int i; + + codec_t *codec = mixer->codecs[cad]; + widget_t *widget; + + if (codec == NULL) + return 0; + + if (wid >= MAX_WIDGETS) + { + cmn_err (CE_WARN, "Too many widgets for codec %d (%d/%d)\n", cad, wid, + MAX_WIDGETS); + return 0; + } + + mixer->ncontrols++; + + codec->nwidgets = wid + 1; + widget = &codec->widgets[wid]; + + widget->cad = cad; + widget->wid = wid; + + widget->rgbcolor = 0; + + widget->group_type = group_type; + + DDB (cmn_err (CE_CONT, " * Widget %02x, type %d\n", wid, group_type)); + + if (!corb_read + (mixer, cad, wid, 0, GET_PARAMETER, HDA_WIDGET_CAPS, &widget_caps, &b)) + { + cmn_err (CE_WARN, "GET_PARAMETER HDA_WIDGET_CAPS failed\n"); + return 0; + } + + if (widget_caps & WCAP_AMP_CAP_OVERRIDE) /* Amp param override? */ + { + if (!corb_read (mixer, widget->cad, widget->wid, 0, + GET_PARAMETER, HDA_OUTPUTAMP_CAPS, &outamp_caps, &b)) + { + cmn_err (CE_WARN, "GET_PARAMETER HDA_OUTPUTAMP_CAPS failed\n"); + return -EIO; + } + widget->outamp_caps = outamp_caps; + + if (!corb_read (mixer, widget->cad, widget->wid, 0, + GET_PARAMETER, HDA_INPUTAMP_CAPS, &inamp_caps, &b)) + { + cmn_err (CE_WARN, "GET_PARAMETER HDA_INPUTAMP_CAPS failed\n"); + return -EIO; + } + widget->inamp_caps = inamp_caps; + } + else + { + widget->outamp_caps = outamp_caps = codec->default_outamp_caps; + widget->inamp_caps = inamp_caps = codec->default_inamp_caps; + } + + if (!corb_read (mixer, cad, wid, 0, GET_POWER_STATE, 0, &pstate, &b)) + return 0; + + /* power up each of the widgets if there is a Power Capability (1<<10) */ + if (widget_caps & WCAP_POWER_CTL) + corb_write (mixer, cad, wid, 0, SET_POWER_STATE, 0); + + widget->widget_caps = widget_caps; + + wid_type = (widget_caps >> 20) & 0x0f; + DDB (cmn_err + (CE_CONT, "\tWidget type %d (%s)(%s)\n", wid_type, + widget_types[wid_type], widget_id[wid_type])); + DDB (cmn_err (CE_CONT, "\tPower State %d\n", pstate)); + + if (widget_caps & WCAP_CONN_LIST) + { + unsigned int clen; + /* Handle connection list */ + + if (!corb_read + (mixer, cad, wid, 0, GET_PARAMETER, HDA_CONNLIST_LEN, &clen, &b)) + { + cmn_err (CE_WARN, "GET_PARAMETER HDA_CONNLIST_LEN failed\n"); + return 0; + } + + if (clen & 0x80) + { + cmn_err (CE_WARN, "Long form connection list not supported\n"); + return 0; + } + + if (clen > 0) + { + if (clen > MAX_CONN) + { + cmn_err (CE_WARN, "Too many connections\n"); + return 0; + } + + DDB (cmn_err (CE_CONT, "\tConn list (%d): ", clen)); + + for (i = 0; i < clen; i += 4) + { + int j; + unsigned int a; + + if (!corb_read + (mixer, cad, wid, 0, GET_CONNECTION_LIST_ENTRY, i, &a, &b)) + { + cmn_err (CE_WARN, "GET_CONNECTION_LIST_ENTRY failed\n"); + return 0; + } + + for (j = 0; j < 4 && (i + j) < clen; j++) + { + int v, is_range = 0; + + if (widget->nconn >= MAX_CONN) + { + cmn_err (CE_WARN, + "Too many connections for widget %d (%d)\n", + widget->wid, widget->nconn); + break; + } + + v = (a >> (j * 8)) & 0xff; + DDB (cmn_err (CE_CONT, "%d ", v)); + + if (v & 0x80) + { + is_range = 1; + v &= ~0x80; + } + + if (v < 0 || v >= MAX_WIDGETS) + { + cmn_err (CE_NOTE, + "Connection %d for widget %d is out of range (%d) - Skipped\n", + j, widget->wid, v); + continue; + } + + if (is_range) /* Range: prev...v */ + { + int x; + codec->widgets[v].references[codec->widgets[v]. + refcount++] = wid; + + if (widget->nconn < 1) + { + cmn_err (CE_CONT, + "Bad range connection for widget %d\n", + widget->wid); + continue; + } + + for (x = widget->connections[widget->nconn - 1] + 1; + x <= v; x++) + { + if (widget->nconn >= MAX_CONN) + { + cmn_err (CE_WARN, + "Too many connectionsi(B) for widget %d (%d)\n", + widget->wid, widget->nconn); + break; + } + + widget->connections[widget->nconn++] = x; + codec->widgets[x].references[codec->widgets[x]. + refcount++] = wid; + } + } + else + { + widget->connections[widget->nconn++] = v; + codec->widgets[v].references[codec->widgets[v]. + refcount++] = wid; + } + } + } + + DDB (cmn_err (CE_CONT, "\n")); + } + } + + widget->wid_type = wid_type; + strcpy (widget->name, widget_id[wid_type]); + +/* + * Handle widget based on its type. + */ + + switch (wid_type) + { + case NT_PIN: + if (!attach_pin_widget (mixer, codec, widget, cad, wid, group_type)) + return 0; + break; + } + + return 1; +} + + /*ARGSUSED*/ static int +attach_function_group (hdaudio_mixer_t * mixer, int cad, int wid, + int group_type) +{ + unsigned int a, b, gt; + int i, first_node, num_nodes; + codec_t *codec = mixer->codecs[cad]; + + if (codec == NULL) + return 0; + cmn_err (CE_CONT, "Attach function group, cad=%02x, wid=%02x\n", cad, wid); + + if (!corb_read (mixer, cad, wid, 0, + GET_PARAMETER, HDA_OUTPUTAMP_CAPS, + &codec->default_outamp_caps, &b)) + { + cmn_err (CE_WARN, "GET_PARAMETER HDA_OUTPUTAMP_CAPS failed\n"); + return 0; + } + + if (!corb_read (mixer, cad, wid, 0, + GET_PARAMETER, HDA_INPUTAMP_CAPS, + &codec->default_inamp_caps, &b)) + { + cmn_err (CE_WARN, "GET_PARAMETER HDA_INPUTTAMP_CAPS failed\n"); + return 0; + } + + if (!corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_NODE_COUNT, &a, &b)) + { + cmn_err (CE_WARN, "GET_PARAMETER HDA_NODE_COUNT2 failed\n"); + return 0; + } + + first_node = (a >> 16) & 0xff; + num_nodes = a & 0xff; + + corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_GROUP_TYPE, >, &b); + gt &= 0xff; + + DDB (cmn_err + (CE_CONT, + " * Function group %d First node %d, num nodes %d, group type %x\n", + wid, first_node, num_nodes, gt)); +/* + * Ignore other than audio function groups. Codecs probably allocate + * higher widget number for the modem group than the audio group. So in this + * way we can have smaller MAX_WIDGETS which in turn conserves memory. + */ + if (gt != group_type) + return 0; + + if (corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_PCM_SIZES, &a, &b)) + { + codec->sizes = a; + } + + if (first_node > 0) + for (i = first_node; i < first_node + num_nodes; i++) + if (!attach_widget (mixer, cad, i, gt)) + return 0; + + if (num_nodes >= 1) + codec->active = 1; + return 1; +} +static int +attach_codec (hdaudio_mixer_t * mixer, int cad, char *hw_info, + unsigned int pci_subdevice, int group_type) +{ + unsigned int a, b, x; + int i; + int first_node, num_nodes; + int has_audio_group = 0; + codec_t *codec; + + if (cad >= MAX_CODECS) + { + cmn_err (CE_WARN, "attach_codec: Too many codecs %d\n", cad); + return -EIO; + } + + mixer->ncodecs = cad + 1; + + if (mixer->codecs[cad] == NULL) + { + if ((codec = PMALLOC (mixer->osdev, sizeof (*codec))) == NULL) + { + cmn_err (CE_CONT, "Cannot allocate codec descriptor\n"); + return -ENOMEM; + } + + memset (codec, 0, sizeof (*codec)); + + mixer->codecs[cad] = codec; + } + else + { + codec = mixer->codecs[cad]; + } + + corb_write (mixer, cad, 0, 0, SET_POWER_STATE, 0); /* Power up everything */ + + if (!corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_VENDOR, &a, &b)) + { + if (group_type == 1) + { + sprintf (hw_info, " Codec %2d: Not present\n", cad); + cmn_err (CE_NOTE, + "attach_codec: Codec #%d is not physically present\n", + cad); + } + return -EIO; + } + + codec->vendor_id = a; + +/* + * Find out the primary group list + */ + + if (!corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_NODE_COUNT, &x, &b)) + { + cmn_err (CE_WARN, "GET_PARAMETER HDA_NODE_COUNT3 failed\n"); + return -EIO; + } + + codec->first_node = first_node = (x >> 16) & 0xff; + num_nodes = x & 0xff; + +/* + * Check if this one is an audio codec (has an audio function group) + */ + for (i = first_node; i < first_node + num_nodes; i++) + { + unsigned int gt; + + if (corb_read + (mixer, cad, i, 0, GET_PARAMETER, HDA_GROUP_TYPE, >, &b)); + if ((gt & 0xff) != 1) /* Audio function group */ + continue; + + has_audio_group = 1; + } + +#if 0 +/* + * Find codec specific settings + */ + for (ix = 0; codecs[ix].id != 0; ix++) + if (codecs[ix].id == a) + break; + + DDB (cmn_err + (CE_CONT, "HD audio Codec ID: %08x (%s)\n", a, codecs[ix].name)); + + if (codecs[ix].id == 0) /* Unknown codec */ + { + if (group_type == 1) + sprintf (hw_info, " Codec %2d: Unknown (0x%08x", cad, a); + cmn_err (CE_NOTE, "Unknown HDA codec 0x%08x\n", a); + /* + * Create hexadecimal codec ID + */ + if (has_audio_group && mixer->chip_name == NULL) + if ((mixer->chip_name = PMALLOC (mixer->osdev, 32)) != NULL) + { + sprintf (mixer->chip_name, "0x%08x", a); + } + } + else + { + if (group_type == 1) + sprintf (hw_info, " Codec %2d: %s (0x%08x", cad, codecs[ix].name, a); + } + + if (has_audio_group && mixer->chip_name == NULL) + { + mixer->chip_name = codecs[ix].name; + + } + + if (codecs[ix].remap != NULL) + codec->remap = codecs[ix].remap; + + if (codecs[ix].flags != 0) + codec->vendor_flags = codecs[ix].flags; + + if (codecs[ix].mixer_init != NULL) + codec->mixer_init = codecs[ix].mixer_init; + + codec->multich_map = codecs[ix].multich_map; + codec->codec_desc = &codecs[ix]; +#endif + + if (corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_REVISION, &a, &b)) + { + DDB (cmn_err (CE_CONT, "HDA codec revision %d.%d (%d.%d) (0x%08x)\n", + (a >> 20) & 0xf, + (a >> 16) & 0xf, (a >> 8) & 0xff, a & 0xff, a)); + } + else + DDB (cmn_err (CE_CONT, "Can't get codec revision\n")); + + DDB (cmn_err (CE_CONT, "**** Codec %d ****\n", cad)); + DDB (cmn_err + (CE_CONT, "First group node %d, num nodes %d\n", first_node, + num_nodes)); + + for (i = first_node; i < first_node + num_nodes; i++) + { + corb_read (mixer, cad, i, 0, GET_PARAMETER, HDA_GROUP_TYPE, &a, &b); + if ((a & 0xff) == group_type) /* Proper function group type */ + { + codec->afg = i; + + if (corb_read (mixer, cad, i, 0, GET_SUBSYSTEM_ID, 0, &a, &b)) + { + DDB (cmn_err (CE_CONT, "Subsystem ID = 0x%08x\n", a)); + + if (group_type == 1) + { + /* Append subvendor ID to hw_info */ + hw_info += strlen (hw_info); + sprintf (hw_info, "/0x%08x", a); + } + + codec->subvendor_id = a; + +#if 0 + for (subix = 0; subdevices[subix].id != 0; subix++) + if (subdevices[subix].id == a) + { + if (subdevices[subix].main_id != 0) + if (subdevices[subix].main_id != codec->vendor_id) + continue; + + if (subdevices[subix].pci_subdevice != 0) + if (subdevices[subix].pci_subdevice != pci_subdevice) + continue; + + + DDB (cmn_err + (CE_CONT, "Subdevice known as %s\n", + subdevices[subix].name)); + if (group_type == 1) + { + hw_info += strlen (hw_info); + sprintf (hw_info, " %s", subdevices[subix].name); + } + if (subdevices[subix].remap != NULL) + { + codec->remap = subdevices[subix].remap; + } + + if (subdevices[subix].multich_map != 0) + codec->multich_map = subdevices[subix].multich_map; + if (subdevices[subix].flags != 0) + codec->vendor_flags = subdevices[subix].flags; + if (subdevices[subix].mixer_init != NULL) + { + codec->mixer_init = subdevices[subix].mixer_init; + } + } +#endif + } + break; + } + } + + hw_info += strlen (hw_info); + if (group_type == 1) + strcpy (hw_info, ")\n"); + + for (i = first_node; i < first_node + num_nodes; i++) + { + if (!attach_function_group (mixer, cad, i, group_type)) + continue; + + /* power up the AFG! */ + corb_write (mixer, cad, i, 0, SET_POWER_STATE, 0); + } + +#if 0 + if (has_audio_group) + { + polish_widget_list (mixer, cad); + } + + copy_endpoints (mixer, codec, 0); /* Copy analog endpoints from codec to mixer */ +#endif + + return (has_audio_group) ? 0 : -EIO; +} + +static void +follow_path(hdaudio_mixer_t *mixer, codec_t *codec, widget_t *widget, path_t *path, int use_force) +{ + int i; + + if (widget->used) + return; + + widget->used = 1; + path->widgets[path->nwidgets++] = widget; + if (widget->wid_type == NT_PIN) + { + path->jack_count++; + path->association=widget->association; + path->sequence=widget->sequence; + + /* + * Stop at a pin widget that is not the first widget of the path. + * In this way we don't walk back the input chain from the pin. + */ + + if (path->nwidgets > 1) + return; + } + + if (use_force || path->nwidgets == 1 || widget->nconn == 1 || (widget->nconn>0 && widget->wid_type == NT_SELECT)) + { + for (i=0;i<widget->nconn;i++) + if (!codec->widgets[widget->connections[i]].used) + //if (codec->widgets[widget->connections[i]].refcount < 2) + { + follow_path(mixer, codec, &codec->widgets[widget->connections[i]], path, use_force); + return; + } + } +} + +static void +dump_path(hdaudio_mixer_t *mixer, path_t *path) +{ + int i, j; + + if (path->nwidgets == 0) + return; + + printf("Path (a=%x, s=%x, jc=%d):\n ", path->association, path->sequence, path->jack_count); + + for (i=0;i<path->nwidgets;i++) + { + widget_t *widget = path->widgets[i]; + + printf("\t%02x(%s/%s/nc=%d/rc=%d", widget->wid, widget_id[widget->wid_type], widget->name, widget->nconn, widget->refcount); + printf(")\n"); + + if (widget->widget_caps & WCAP_OUTPUT_AMP_PRESENT) + { + printf("\t\tOutput amp\n"); + } + + if (widget->widget_caps & WCAP_INPUT_AMP_PRESENT) + { + printf("\t\t%d input amp(s)\n", widget->nconn); + + for (j=0;j<widget->nconn;j++) + { + widget_t *in_widget; + + in_widget = &mixer->codecs[widget->cad]-> + widgets[widget->connections[j]]; + printf("\t\t\t%02x(%s/%s/nc=%d/rc=%d)\n", in_widget->wid, widget_id[in_widget->wid_type], in_widget->name, in_widget->nconn, in_widget->refcount); + + in_widget->used=1; + } + } + } + printf("\n"); +} + +static void +store_path(hdaudio_mixer_t *mixer, path_t *path) +{ + if (npaths>=MAX_PATHS) + { + cmn_err(CE_WARN, "Too many paths\n"); + return; + } + + paths[npaths++]=path; +} + +static void +create_path_list(hdaudio_mixer_t *mixer, int cad, int node_type, int use_force) +{ + codec_t *codec; + int wid; + + path_t *path; + +cmn_err(CE_CONT, "Create path list %d (%s)\n", node_type, widget_id[node_type]); + codec=mixer->codecs[cad]; + + for (wid=0;wid<codec->nwidgets;wid++) + if (codec->widgets[wid].wid_type == node_type) + if (!codec->widgets[wid].skip) + if (codec->widgets[wid].wid == wid) + { + path=malloc(sizeof(*path)); + + follow_path(mixer, codec, &codec->widgets[wid], path, use_force); + + if (path->nwidgets>1) + store_path(mixer, path); + else + if (path->nwidgets>0) + path->widgets[0]->used=0; + } + +} + +static void +create_path_list_for_pin(hdaudio_mixer_t *mixer, int cad, int association, int sequence) +{ + codec_t *codec; + int wid; + + path_t *path; + + codec=mixer->codecs[cad]; + + for (wid=0;wid<codec->nwidgets;wid++) + if (codec->widgets[wid].wid_type == NT_PIN && !codec->widgets[wid].skip) + if (codec->widgets[wid].pin_type == PIN_HEADPHONE || codec->widgets[wid].pin_type == PIN_OUT) + if (codec->widgets[wid].wid == wid) + if (codec->widgets[wid].nconn > 0) + if (!codec->widgets[wid].skip) + if (codec->widgets[wid].sequence == sequence) + if (codec->widgets[wid].association == association) + { + path=malloc(sizeof(*path)); + + follow_path(mixer, codec, &codec->widgets[wid], path, 0); + + if (path->nwidgets>1) + store_path(mixer, path); + else + if (path->nwidgets>0) + path->widgets[0]->used=0; + } + +} + +hdaudio_mixer_t * +hdaudio_mixer_create (char *name, void *devc, + oss_device_t * osdev, + hdmixer_write_t writefunc, + hdmixer_read_t readfunc, unsigned int codecmask, + unsigned int vendor_id, unsigned int subvendor_id) +{ + hdaudio_mixer_t *mixer; + int i, func; + int ncodecs = 0; + char tmp[128]; + int mixer_dev; + + char *hw_info = osdev->hw_info; /* For printing hardware information */ + + if ((mixer = PMALLOC (osdev, sizeof (*mixer))) == NULL) + { + cmn_err (CE_WARN, "hdaudio_mixer_create: Out of memory\n"); + return NULL; + } + + memset (mixer, 0, sizeof (*mixer)); + + mixer->devc = devc; + mixer->osdev = osdev; + mixer->mixer_dev = 0; + strncpy (mixer->name, name, sizeof (mixer->name) - 1); + mixer->name[sizeof (mixer->name) - 1] = 0; + + for (i = 0; i < MAX_CODECS; i++) + mixer->codecs[i] = NULL; + +#ifdef _KERNEL + mixer->read = readfunc; + mixer->write = writefunc; +#endif + + mixer->codecmask = codecmask; + + sprintf (hw_info, "HD Audio controller %s\n" + "Vendor ID 0x%08x\n" + "Subvendor ID 0x%08x\n", name, vendor_id, subvendor_id); + hw_info += strlen (hw_info); + +/* + * Search first all audio function groups for all codecs and then + * handle modem function groups. + */ + for (func = 1; func <= 2; func++) + for (i = 0; i < 16; i++) + if (mixer->codecmask & (1 << i)) + { + if (attach_codec (mixer, i, hw_info, subvendor_id, func) >= 0) + ncodecs++; + hw_info += strlen (hw_info); + } + + for (i = 0; i < 16; i++) + if (mixer->codecmask & (1 << i)) + { + int association, sequence; + +printf("*** Codec %d\n", i); + for (association=1;association<16;association++) + for (sequence=0;sequence<16;sequence++) + create_path_list_for_pin(mixer, i, association, sequence); + + create_path_list(mixer, i, NT_ADC, 0); + create_path_list(mixer, i, NT_MIXER, 1); + create_path_list(mixer, i, NT_SELECT, 1); + } + + for (i=0;i<npaths;i++) + dump_path(mixer, paths[i]); + +#if 1 + printf("\n\nOther widgets:\n"); + + for (i = 0; i < 16; i++) + if (mixer->codecmask & (1 << i)) + { + int wid; + + for (wid=0;wid<mixer->codecs[i]->nwidgets;wid++) + { + widget_t *widget = &mixer->codecs[i]->widgets[wid]; + + if (widget->wid != wid) + continue; + + if (widget->used || widget->skip) + continue; + printf("Codec %d, Widget %02x %s/%s/%d\n", widget->cad, widget->wid, widget_id[widget->wid_type], widget->name, widget->nconn); + } + } +#endif + + return mixer; +} + +int +main (int argc, char *argv[]) +{ + unsigned int rev, b; + int first_node, num_nodes; + int i; + int cad = 0; + + int codecmask = 0; + + oss_device_t osdev = { 0 }; + + osdev.hw_info = malloc (256); + + if ((fd = open ("/dev/oss/oss_hdaudio0/pcm0", O_RDWR | O_EXCL, 0)) == -1) + { + perror ("/dev/oss/oss_hdaudio0/pcm0"); + exit (-1); + } + + for (cad = 0; cad < 15; cad++) + if (corb_read (NULL, cad, 0, 0, GET_PARAMETER, HDA_REVISION, &rev, &b)) + { +#if 1 + printf ("Codec %2d: HD codec revision %d.%d (%d.%d) (0x%08x)\n", cad, + (rev >> 20) & 0xf, (rev >> 16) & 0xf, (rev >> 8) & 0xff, + rev & 0xff, rev); +#endif + codecmask |= (1 << cad); + } + + hdaudio_mixer_create ("ACME hdaudio", NULL, + &osdev, NULL, NULL, codecmask, 0x12345678, 0x8754321); + + //printf("HW Info: %s\n", osdev.hw_info); + exit (0); +} |