Commit 7bbd8277 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Linus Torvalds

[PATCH] ppc64: very basic desktop g5 sound support

This patch hacks the current PowerMac Alsa driver to add some basic support
of analog sound output to some desktop G5s.  It has severe limitations
though:

 - Only 44100Khz 16 bits
 - Only work on G5 models using a TAS3004 analog code, that is early
   single CPU desktops and all dual CPU desktops at this date, but none
   of the more recent ones like iMac G5.
 - It does analog only, no digital/SPDIF support at all, no native
   AC3 support

Better support would require a complete rewrite of the driver (which I am
working on, but don't hold your breath), to properly support the diversity
of apple sound HW setup, including dual codecs, several i2s busses, all the
new codecs used in the new machines, proper clock switching with digital,
etc etc etc...

This patch applies on top of the other PowerMac sound patches I posted in
the past couple of days (new powerbook support and sleep fixes).  

Note: This is a FAQ entry for PowerMac sound support with TI codecs: They
have a feature called "DRC" which is automatically enabled for the internal
speaker (at least when auto mute control is enabled) which will cause your
sound to fade out to nothing after half a second of playback if you don't
set a proper "DRC Range" in the mixer.  So if you have a problem like that,
check alsamixer and raise your DRC Range to something reasonable.

Note2: This patch will also add auto-mute of the speaker when line-out jack
is used on some earlier desktop G4s (and on the G5) in addition to the
headphone jack.  If that behaviour isn't what you want, just disable
auto-muting and use the manual mute controls in alsamixer.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b20af5f5
...@@ -74,8 +74,7 @@ static DEFINE_SPINLOCK(feature_lock __pmacdata); ...@@ -74,8 +74,7 @@ static DEFINE_SPINLOCK(feature_lock __pmacdata);
*/ */
struct macio_chip macio_chips[MAX_MACIO_CHIPS] __pmacdata; struct macio_chip macio_chips[MAX_MACIO_CHIPS] __pmacdata;
struct macio_chip* __pmac struct macio_chip* __pmac macio_find(struct device_node* child, int type)
macio_find(struct device_node* child, int type)
{ {
while(child) { while(child) {
int i; int i;
...@@ -88,6 +87,7 @@ macio_find(struct device_node* child, int type) ...@@ -88,6 +87,7 @@ macio_find(struct device_node* child, int type)
} }
return NULL; return NULL;
} }
EXPORT_SYMBOL_GPL(macio_find);
static const char* macio_names[] __pmacdata = static const char* macio_names[] __pmacdata =
{ {
......
...@@ -64,8 +64,7 @@ static DEFINE_SPINLOCK(feature_lock __pmacdata); ...@@ -64,8 +64,7 @@ static DEFINE_SPINLOCK(feature_lock __pmacdata);
*/ */
struct macio_chip macio_chips[MAX_MACIO_CHIPS] __pmacdata; struct macio_chip macio_chips[MAX_MACIO_CHIPS] __pmacdata;
struct macio_chip* __pmac struct macio_chip* __pmac macio_find(struct device_node* child, int type)
macio_find(struct device_node* child, int type)
{ {
while(child) { while(child) {
int i; int i;
...@@ -78,6 +77,7 @@ macio_find(struct device_node* child, int type) ...@@ -78,6 +77,7 @@ macio_find(struct device_node* child, int type)
} }
return NULL; return NULL;
} }
EXPORT_SYMBOL_GPL(macio_find);
static const char* macio_names[] __pmacdata = static const char* macio_names[] __pmacdata =
{ {
...@@ -250,6 +250,30 @@ static long __pmac g5_eth_phy_reset(struct device_node* node, long param, long v ...@@ -250,6 +250,30 @@ static long __pmac g5_eth_phy_reset(struct device_node* node, long param, long v
return 0; return 0;
} }
static long __pmac g5_i2s_enable(struct device_node *node, long param, long value)
{
/* Very crude implementation for now */
struct macio_chip* macio = &macio_chips[0];
unsigned long flags;
if (value == 0)
return 0; /* don't disable yet */
LOCK(flags);
MACIO_BIS(KEYLARGO_FCR3, KL3_CLK45_ENABLE | KL3_CLK49_ENABLE |
KL3_I2S0_CLK18_ENABLE);
udelay(10);
MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_I2S0_CELL_ENABLE |
K2_FCR1_I2S0_CLK_ENABLE_BIT | K2_FCR1_I2S0_ENABLE);
udelay(10);
MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_I2S0_RESET);
UNLOCK(flags);
udelay(10);
return 0;
}
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static long __pmac g5_reset_cpu(struct device_node* node, long param, long value) static long __pmac g5_reset_cpu(struct device_node* node, long param, long value)
{ {
...@@ -337,6 +361,7 @@ static struct feature_table_entry g5_features[] __pmacdata = { ...@@ -337,6 +361,7 @@ static struct feature_table_entry g5_features[] __pmacdata = {
{ PMAC_FTR_READ_GPIO, g5_read_gpio }, { PMAC_FTR_READ_GPIO, g5_read_gpio },
{ PMAC_FTR_WRITE_GPIO, g5_write_gpio }, { PMAC_FTR_WRITE_GPIO, g5_write_gpio },
{ PMAC_FTR_GMAC_PHY_RESET, g5_eth_phy_reset }, { PMAC_FTR_GMAC_PHY_RESET, g5_eth_phy_reset },
{ PMAC_FTR_SOUND_CHIP_ENABLE, g5_i2s_enable },
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
{ PMAC_FTR_RESET_CPU, g5_reset_cpu }, { PMAC_FTR_RESET_CPU, g5_reset_cpu },
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
......
...@@ -88,7 +88,7 @@ struct dbdma_cmd { ...@@ -88,7 +88,7 @@ struct dbdma_cmd {
#define WAIT_ALWAYS 3 /* always wait */ #define WAIT_ALWAYS 3 /* always wait */
/* Align an address for a DBDMA command structure */ /* Align an address for a DBDMA command structure */
#define DBDMA_ALIGN(x) (((unsigned)(x) + sizeof(struct dbdma_cmd) - 1) \ #define DBDMA_ALIGN(x) (((unsigned long)(x) + sizeof(struct dbdma_cmd) - 1) \
& -sizeof(struct dbdma_cmd)) & -sizeof(struct dbdma_cmd))
/* Useful macros */ /* Useful macros */
......
...@@ -228,6 +228,11 @@ ...@@ -228,6 +228,11 @@
#define K2_FCR1_PCI1_BUS_RESET_N 0x00000010 #define K2_FCR1_PCI1_BUS_RESET_N 0x00000010
#define K2_FCR1_PCI1_SLEEP_RESET_EN 0x00000020 #define K2_FCR1_PCI1_SLEEP_RESET_EN 0x00000020
#define K2_FCR1_I2S0_CELL_ENABLE 0x00000400
#define K2_FCR1_I2S0_RESET 0x00000800
#define K2_FCR1_I2S0_CLK_ENABLE_BIT 0x00001000
#define K2_FCR1_I2S0_ENABLE 0x00002000
#define K2_FCR1_PCI1_CLK_ENABLE 0x00004000 #define K2_FCR1_PCI1_CLK_ENABLE 0x00004000
#define K2_FCR1_FW_CLK_ENABLE 0x00008000 #define K2_FCR1_FW_CLK_ENABLE 0x00008000
#define K2_FCR1_FW_RESET_N 0x00010000 #define K2_FCR1_FW_RESET_N 0x00010000
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include "pmac.h" #include "pmac.h"
...@@ -35,7 +37,7 @@ struct snd_pmac_beep { ...@@ -35,7 +37,7 @@ struct snd_pmac_beep {
int hz; int hz;
int nsamples; int nsamples;
short *buf; /* allocated wave buffer */ short *buf; /* allocated wave buffer */
unsigned long addr; /* physical address of buffer */ dma_addr_t addr; /* physical address of buffer */
struct input_dev dev; struct input_dev dev;
}; };
...@@ -217,12 +219,8 @@ int __init snd_pmac_attach_beep(pmac_t *chip) ...@@ -217,12 +219,8 @@ int __init snd_pmac_attach_beep(pmac_t *chip)
return -ENOMEM; return -ENOMEM;
memset(beep, 0, sizeof(*beep)); memset(beep, 0, sizeof(*beep));
beep->buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); beep->buf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
if (! beep->buf) { &beep->addr, GFP_KERNEL);
kfree(beep);
return -ENOMEM;
}
beep->addr = virt_to_bus(beep->buf);
beep->dev.evbit[0] = BIT(EV_SND); beep->dev.evbit[0] = BIT(EV_SND);
beep->dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); beep->dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
...@@ -255,7 +253,8 @@ void snd_pmac_detach_beep(pmac_t *chip) ...@@ -255,7 +253,8 @@ void snd_pmac_detach_beep(pmac_t *chip)
{ {
if (chip->beep) { if (chip->beep) {
input_unregister_device(&chip->beep->dev); input_unregister_device(&chip->beep->dev);
kfree(chip->beep->buf); dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
chip->beep->buf, chip->beep->addr);
kfree(chip->beep); kfree(chip->beep);
chip->beep = NULL; chip->beep = NULL;
} }
......
This diff is collapsed.
...@@ -60,7 +60,8 @@ typedef struct snd_pmac_dbdma pmac_dbdma_t; ...@@ -60,7 +60,8 @@ typedef struct snd_pmac_dbdma pmac_dbdma_t;
* DBDMA space * DBDMA space
*/ */
struct snd_pmac_dbdma { struct snd_pmac_dbdma {
unsigned long addr; dma_addr_t dma_base;
dma_addr_t addr;
struct dbdma_cmd __iomem *cmds; struct dbdma_cmd __iomem *cmds;
void *space; void *space;
int size; int size;
...@@ -101,6 +102,7 @@ struct snd_pmac { ...@@ -101,6 +102,7 @@ struct snd_pmac {
/* h/w info */ /* h/w info */
struct device_node *node; struct device_node *node;
struct pci_dev *pdev;
unsigned int revision; unsigned int revision;
unsigned int manufacturer; unsigned int manufacturer;
unsigned int subframe; unsigned int subframe;
...@@ -110,6 +112,7 @@ struct snd_pmac { ...@@ -110,6 +112,7 @@ struct snd_pmac {
unsigned int has_iic : 1; unsigned int has_iic : 1;
unsigned int is_pbook_3400 : 1; unsigned int is_pbook_3400 : 1;
unsigned int is_pbook_G3 : 1; unsigned int is_pbook_G3 : 1;
unsigned int is_k2 : 1;
unsigned int can_byte_swap : 1; unsigned int can_byte_swap : 1;
unsigned int can_duplex : 1; unsigned int can_duplex : 1;
...@@ -157,6 +160,7 @@ struct snd_pmac { ...@@ -157,6 +160,7 @@ struct snd_pmac {
snd_kcontrol_t *speaker_sw_ctl; snd_kcontrol_t *speaker_sw_ctl;
snd_kcontrol_t *drc_sw_ctl; /* only used for tumbler -ReneR */ snd_kcontrol_t *drc_sw_ctl; /* only used for tumbler -ReneR */
snd_kcontrol_t *hp_detect_ctl; snd_kcontrol_t *hp_detect_ctl;
snd_kcontrol_t *lineout_sw_ctl;
/* lowlevel callbacks */ /* lowlevel callbacks */
void (*set_format)(pmac_t *chip); void (*set_format)(pmac_t *chip);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment