📄 ak470x.c
字号:
static int ak470x_audio_volume_set (struct i2c_client *client, AK470X_AUDIO_IOCTL_VOLUME_t *volume){ AK470X_AUDIO_CHANNEL_NUMBER_t channel; int retValue = -EINVAL; u8 data_byte = 0; int gain; // Check out channel number channel = volume->audio_channel; if (!ak470x_audio_channel_number_isvalid(channel)) return retValue; // SET Audio Master Volume (L+R)/2 -> (Global) gain = ((int)volume->master_left + (int)volume->master_right) / 2; // Enforce boundaries data_byte = (u8) ak470x_enforce_volume_boundaries (gain, AK470X_gain_min, AK470X_gain_max); // I2C buffered I/O retValue = ak470x_audio_write (client, AK470X_ADDR_VOLUME, &data_byte); if (retValue) { dprintk(1, KERN_INFO "ak470x.c: Failed to set volume for audio channel %d (0x%X)\n", (int) channel, retValue); return retValue; } // Refresh back buffer ak470x_RegBackBuffer[AK470X_VOLUME] = data_byte; return retValue;}// ----------------------- AK470X Audio Function (Global init) ---------------static int ak470x_audio_init (struct i2c_client *client){ int retValue = -EINVAL; int channel; for (channel=AK470X_AUDIO_CHANNEL_NUMBER_MIN; channel<=AK470X_AUDIO_CHANNEL_NUMBER_MAX; channel++) { AK470X_AUDIO_IOCTL_VOLUME_t *pvolume, volume; pvolume = &ak470x_delta_volume [ak470x_audio_channel_number_to_index(channel)]; // Preset associative buffer pvolume->audio_channel = channel; pvolume->master_left = (AK470X_AUDIO_MASTER_VOLUME_t) ak470x_internal_gain (AK470X_AUDIO_EXTERNAL_VOLUME_MID); pvolume->master_right = (AK470X_AUDIO_MASTER_VOLUME_t) ak470x_internal_gain (AK470X_AUDIO_EXTERNAL_VOLUME_MID); pvolume->external_left = AK470X_AUDIO_EXTERNAL_VOLUME_MID; pvolume->external_right = AK470X_AUDIO_EXTERNAL_VOLUME_MID; // Default volume : AK470X_AUDIO_EXTERNAL_VOLUME_MID (-60+((34/2)x2) = -26dB std) // Set volume retValue = ak470x_audio_volume_set (client, pvolume); if (retValue) break; // Retrieve actual AK470X chip volume volume.audio_channel = channel; retValue = ak470x_audio_volume_get (client, &volume); if (retValue) break; // Pick associated buffered external value if match (LEFT) if (volume.master_left!=pvolume->master_left) { // Else resync with chipset value (LEFT) pvolume->master_left = volume.master_left; pvolume->external_left = ak470x_standard_volume (volume.master_left); } // Pick associated buffered external value if match (RIGHT) if (volume.master_right!=pvolume->master_right) { // Else resync with fresh chipset value (RIGHT) pvolume->master_right = volume.master_right; pvolume->external_right = ak470x_standard_volume (volume.master_right); } } return retValue;}// ****************************************************************************// * *// * M O D U L E S E C T I O N *// * *// ****************************************************************************// ----------------------- AK470X IOCTL switch (Commands) ----------------static int ak470x_command ( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ dprintk(1, KERN_INFO "Got AK470X Command : 0x%X\n", cmd); switch (cmd) { //---------------------------------------------------------------------- // AV Switch IOCTLs //---------------------------------------------------------------------- case AK470X_IOCTL_SWITCH_MODE_SET: return ak470x_switch_set ((const AK470X_SWITCH_IOCTL_MODE_t*)arg); break; case AK470X_IOCTL_SWITCH_MODE_GET: return ak470x_switch_get ((AK470X_SWITCH_IOCTL_MODE_t*)arg); break; //---------------------------------------------------------------------- // Audio IOCTLs //---------------------------------------------------------------------- case AK470X_IOCTL_AUDIO_MODE_SET: return ak470x_set_audio_mode (ak470x_client, (AK470X_AUDIO_IOCTL_MODE_t*) arg); break; case AK470X_IOCTL_AUDIO_VOLUME_GET: return ak470x_audio_volume_get (ak470x_client, (AK470X_AUDIO_IOCTL_VOLUME_t*) arg); break; case AK470X_IOCTL_AUDIO_VOLUME_SET: return ak470x_audio_volume_set (ak470x_client, (AK470X_AUDIO_IOCTL_VOLUME_t*) arg); break; // Unimplemented IOCTL default: dprintk(1, KERN_INFO "unknown ak470x??(%d)\n", cmd); return -EINVAL; } return 0;}// ----------------------- AK470X I2C Detection Function (Detect) ------------static int ak470x_detect_client (struct i2c_adapter *adapter, int address, int kind){ int rv; u8 value; dprintk(1, KERN_INFO "ak470x.c: detecting ak470x client on address 0x%x\n", address << 1); /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { return 0; } /* Create the I2C client data structure */ ak470x_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); if (ak470x_client == 0) { return -ENOMEM; } memset(ak470x_client, 0, sizeof(struct i2c_client)); ak470x_client->addr = address; ak470x_client->adapter = adapter; ak470x_client->driver = &i2c_driver_ak470x; snprintf(I2C_NAME(ak470x_client), sizeof(I2C_NAME(ak470x_client)) - 1, "ak470x"); /* Attach the AV switch to the I2C adapter */ rv = i2c_attach_client(ak470x_client); if (rv) { dprintk(1, KERN_INFO "ak470x.c: Failed to attach to client on adapter %s (0x%x) - 0x%02X\n", I2C_NAME(adapter), adapter->id, rv); kfree(ak470x_client); return rv; } if (ak470x_initialised) { return 0; } /* Previous way of determining variant caused a popping noise on */ /* the output. So be a bit more explicit in the device selection. */#if defined (CONFIG_NXP_STB225) is_ak4702 = 0;#else is_ak4702 = 1;#endif ak470x_initialised = 1; dprintk(1, KERN_INFO "ak470x.c: found device (%s) on adapter %s (0x%x) - 0x%02X\n", is_ak4702 ? "AK4702" : "AK4706", I2C_NAME(adapter), adapter->id, rv); return 0;}// ----------------------- AK470X I2C Detection Function (Attach) --------static int ak470x_attach_adapter (struct i2c_adapter *adapter){ return i2c_probe(adapter, &addr_data, &ak470x_detect_client);}// ----------------------- AK470X I2C Detection Function (Detach) --------static int ak470x_detach_client (struct i2c_client *client){ int err; err = i2c_detach_client(client); if (err) { return err; } kfree(client); return 0;}#ifdef CONFIG_PMstatic int ak470x_suspend(struct i2c_client *client, pm_message_t mesg){ keep_control_status = ak470x_read(ak470x_client, AK470X_ADDR_CONTROL); ak470x_write(ak470x_client, AK470X_ADDR_CONTROL, AK470X_POWER_DOWN); return 0;}static int ak470x_resume(struct i2c_client *client){ ak470x_write(ak470x_client, AK470X_ADDR_CONTROL, keep_control_status); return 0;}#endif// ----------------------- MODULE INIT ------------------------------------static int __init ak470x_init (void){ int result; ak470x_initialised = 0; /* Register a misc device called "ak470x". */ result = misc_register( &ak470x_device ); if (result < 0) { printk("can't register misc device (minor %d)!\n", ak470x_device.minor ); return result; } result = i2c_add_driver(&i2c_driver_ak470x); // Initialise the AV switch (loop through mode by default) ak470x_switch_init (ak470x_client); // Initialise the Audio mixer (50% volume by default) ak470x_audio_init (ak470x_client); if (result == 0) { printk ("%s (%s-%s) - Device %s Loopthrough %s\n", THIS_MODULE_DESCRIPTION, __DATE__, __TIME__, is_ak4702 ? "AK4702" : "AK4706", loopthrough ? "enabled" : "disabled"); ak470x_installed = 1; } else { printk ("%s (%s-%s) - Scart Switch not available - Dummy Driver only.\n", THIS_MODULE_DESCRIPTION, __DATE__, __TIME__); ak470x_installed = 0; } return 0;}// ----------------------- MODULE EXIT ------------------------------------static void __exit ak470x_exit (void){ misc_deregister(&ak470x_device); i2c_del_driver(&i2c_driver_ak470x); printk("Shutting down %s\n", THIS_MODULE_DESCRIPTION);}// ****************************************************************************// * *// * P U B L I C O B J E C T S *// * *// ****************************************************************************// -----------------------------------------------------------------------------// Exported functions// -----------------------------------------------------------------------------// ----------------------- AK470X IOCTL wrapper (Commands) ----------------int ak470x_exec (unsigned int cmd, void *p_object){ return ak470x_command (NULL, NULL, cmd, (unsigned long) p_object);}// ----------------------- AK470X exported function ------------------------int ak470x_std_volume_get (int channel, int *p_left, int *p_right){ int retValue = -EINVAL; AK470X_AUDIO_IOCTL_VOLUME_t volume; AK470X_AUDIO_IOCTL_VOLUME_t *pvolume; pvolume = &ak470x_delta_volume [ak470x_audio_channel_number_to_index(channel)]; volume.audio_channel = channel; volume.master_left = AK470X_AUDIO_INTERNAL_GAIN_MIN; volume.master_right = AK470X_AUDIO_INTERNAL_GAIN_MIN; retValue = ak470x_exec (AK470X_IOCTL_AUDIO_VOLUME_GET, &volume); if (!retValue) // If ok ... { // Pick associated buffered external value if match (LEFT) if (volume.master_left!=pvolume->master_left) { // Else resync with chipset value (LEFT) pvolume->external_left = ak470x_standard_volume (volume.master_left); } // Pick associated buffered external value if match (RIGHT) if (volume.master_right!=pvolume->master_right) { // Else resync with fresh chipset value (RIGHT) pvolume->external_right = ak470x_standard_volume (volume.master_right); } if (p_left != NULL) *p_left = pvolume->external_left; if (p_right != NULL) *p_right = pvolume->external_right; } return retValue;}// ----------------------- AK470X exported function ------------------------int ak470x_std_volume_set (int channel, int left, int right){ int retValue = -EINVAL; AK470X_AUDIO_IOCTL_VOLUME_t *pvolume; pvolume = &ak470x_delta_volume [ak470x_audio_channel_number_to_index(channel)]; // Set channel ID pvolume->audio_channel = channel; // Store requested external scale volumes (values associated with internal ones) pvolume->external_left = left; pvolume->external_right = right; // Set internal scale volumes (values associated with external ones) pvolume->master_left = (AK470X_AUDIO_MASTER_VOLUME_t) ak470x_internal_gain (left); pvolume->master_right = (AK470X_AUDIO_MASTER_VOLUME_t) ak470x_internal_gain (right); retValue = ak470x_exec (AK470X_IOCTL_AUDIO_VOLUME_SET, pvolume); return retValue;}// ----------------------- AK470X exported function ------------------------int ak470x_std_volume_range_get (int channel, int *p_min, int *p_max){ int retValue = -EINVAL; if (ak470x_audio_channel_number_isvalid(channel)) // If channel# ok ... { if (p_min != NULL) *p_min = AK470X_vol_min; if (p_max != NULL) *p_max = AK470X_vol_max; retValue = 0; } return retValue;}// ****************************************************************************// * *// * E X P O R T E D S Y M B O L S *// * *// ****************************************************************************EXPORT_SYMBOL(ak470x_exec);EXPORT_SYMBOL(ak470x_std_volume_get);EXPORT_SYMBOL(ak470x_std_volume_set);EXPORT_SYMBOL(ak470x_std_volume_range_get);EXPORT_SYMBOL(ak470x_switch_set);EXPORT_SYMBOL(ak470x_switch_get);EXPORT_SYMBOL(ak470x_switch_installed);// ----------------------- MODULE MANAGEMENT --------------------------------module_init(ak470x_init);module_exit(ak470x_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -