/***************************************************************/
/*                                                             */
/* Program name: fixcmi.c                                      */
/*                                                             */
/* Simple program to adjust the analog CD-in right and left    */
/* channels of the CMI8330 sound chip                          */
/*                                                             */
/* Author: Luiz T. S. Mendes (luizt@fisica.ufmg.br)            */
/*                                                             */
/* Version: 1.0                Date: Nov 23, 1998              */
/*                                                             */
/*          1.1                Date: Dec 1, 1998               */
/*                             by Mark W. Vandersteen          */
/*              (mvanders@picknowl.com.au)                     */
/*              Added extra support for enabling Line-In,      */
/*              loud, 3d_surround etc                          */
/*                                                             */
/* DISCLAIMER: This software is provided as it is and comes    */
/*             with absolutely NO WARRANTY . Use it at your    */
/*             own risk.                                       */
/*                                                             */
/* NOTES: (1) This program is meant for the people who already */
/*            have their CMI8330-based sound cards working     */
/*            (except for playing audio CDs, of course). This  */
/*            means that the following two steps should have   */
/*            already succeeded on your linux box:             */
/*                                                             */
/*            - installing the proper Linux sound driver;      */
/*            - configuring the CMI8330 sound chip by means    */
/*              of the isapnptools (see the Web site at        */
/*              http://www.roestock.demon.co.uk/isapnptools/). */
/*              In this configuration the WSS logical device   */
/*              (logical device 0 in the CMI8330) MUST be      */
/*              included, even if the sound card is to be used */
/*              in the SoundBlaster mode.                      */
/*                                                             */
/*            These steps are fairly documented in the Linux   */
/*            HOWTOs and in the above isapnptools WWW site.    */
/*                                                             */
/*        (2) This program have been tested ONLY in my Linux   */
/*            box (Slackware 3.5, kernel 2.0.34). Though it    */
/*            should work in other configurations, there is    */
/*            no way to guarantee that.                        */
/*                                                             */
/*                                                             */
/* INSTRUCTIONS:                                               */
/*                                                             */
/* 1. Change the definition WSS_BASE to match the CMI8330 WSS  */
/*    I/O base address specified in your /etc/isapnp.conf      */
/*                                                             */
/* 2. Save the file and compile it with                        */
/*                                                             */
/*            $ gcc -o fixcmi -O fixcmi.c                      */
/*                                                             */
/*    (CAUTION: the -O option is MANDATORY)                    */
/*                                                             */
/* 3. You must be logged in as root to run the program, or     */
/*    SETUID it as root:                                       */
/*                                                             */
/*            # chmod +s fixcmi                                */
/*                                                             */
/* 4. Run it _after_ configuring the chip by the isapnp        */
/*    command.                                                 */
/*                                                             */
/***************************************************************/

#include <stdio.h>
#include <asm/io.h>
#include <unistd.h>

/* Some definitions, most related to the I/O addressing */

/* The base io address of WSS from isapnp.conf */
#define  WSS_BASE                0x600

#define  CMI8330_WSS_BASE        WSS_BASE+4
#define  CMI8330_WSS_INDEX_ADDR  CMI8330_WSS_BASE
#define  CMI8330_WSS_INDEX_REG   CMI8330_WSS_BASE+1
#define  CMI8330_WSS_STATUS_REG  CMI8330_WSS_BASE+2

#define  CMI8330_EXT_REG1 0x10
#define  CMI8330_EXT_REG2 0x11

/* for register 0x10h (each bit)*/
#define  SURROUND  0x20

/* for reg 0x11h (each bit)
* 0   - mute microphone
* 1-2 - mute CD-in right & left
* 3-4 - mute Line-in right & left
* 5   - Reserved
* 6   - sound output louder
* 7   - S/PDIF-IN channel
*/
#define  MICROPHONE    0x01
#define  CDROM_RIGHT   0x02
#define  CDROM_LEFT    0x04
#define  CDROM         CDROM_LEFT | CDROM_RIGHT
#define  LINEIN_RIGHT  0x08
#define  LINEIN_LEFT   0x10
#define  LINEIN        LINEIN_LEFT | LINEIN_RIGHT
#define  ENABLE_LOUD   0x40
#define  SPDIFIN       0x80


/* Will enable access to Indirect Registers of the chip
 * bit 6 of register 0x0C en/disables access */
void cmi8330_enable_write(int onoff)
{
  outb(0x0C, CMI8330_WSS_INDEX_ADDR);
  if (onoff)
    outb(0x40,  CMI8330_WSS_INDEX_REG); /* set bit */
  else
    outb(0x00,  CMI8330_WSS_INDEX_REG); /* clear bit */
}

void cmi8330_set_reg(int reg, short int data)
{
  short int tmp;

  outb(reg, CMI8330_WSS_INDEX_ADDR);
  tmp = inb(CMI8330_WSS_INDEX_REG) | data;
  outb(tmp, CMI8330_WSS_INDEX_REG);
}


main()
{
  int status, i;

  /* Set the required I/O privilege level for accessing the I/O ports. */

  status=iopl(3);
  if (status != 0)
    {
      perror("FIXCMI: IOPL() Error\n");
      exit(1);
    }

  /* Check the CMI8330 WSS register locations */

  if (inb(CMI8330_WSS_STATUS_REG) != 0xCC)
    {
      printf("FIXCMI: Invalid WSS I/O address\n");
      exit(1);
    }

  /* Now set the required bits for turning on whatever */

  cmi8330_enable_write(1);

  cmi8330_set_reg( CMI8330_EXT_REG1, SURROUND);
  cmi8330_set_reg( CMI8330_EXT_REG2, CDROM | LINEIN | ENABLE_LOUD | MICROPHONE);

  cmi8330_enable_write(0);
}
