18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * wm8739 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 T. Adachi <tadachi@tadachi-net.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl> 88c2ecf20Sopenharmony_ci * - Cleanup 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <linux/i2c.h> 178c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 188c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 198c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("wm8739 driver"); 228c2ecf20Sopenharmony_ciMODULE_AUTHOR("T. Adachi, Hans Verkuil"); 238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int debug; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-1)"); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cienum { 358c2ecf20Sopenharmony_ci R0 = 0, R1, 368c2ecf20Sopenharmony_ci R5 = 5, R6, R7, R8, R9, R15 = 15, 378c2ecf20Sopenharmony_ci TOT_REGS 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct wm8739_state { 418c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 428c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 438c2ecf20Sopenharmony_ci struct { 448c2ecf20Sopenharmony_ci /* audio cluster */ 458c2ecf20Sopenharmony_ci struct v4l2_ctrl *volume; 468c2ecf20Sopenharmony_ci struct v4l2_ctrl *mute; 478c2ecf20Sopenharmony_ci struct v4l2_ctrl *balance; 488c2ecf20Sopenharmony_ci }; 498c2ecf20Sopenharmony_ci u32 clock_freq; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic inline struct wm8739_state *to_state(struct v4l2_subdev *sd) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return container_of(sd, struct wm8739_state, sd); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 678c2ecf20Sopenharmony_ci int i; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (reg < 0 || reg >= TOT_REGS) { 708c2ecf20Sopenharmony_ci v4l2_err(sd, "Invalid register R%d\n", reg); 718c2ecf20Sopenharmony_ci return -1; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 778c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, 788c2ecf20Sopenharmony_ci (reg << 1) | (val >> 8), val & 0xff) == 0) 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); 818c2ecf20Sopenharmony_ci return -1; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int wm8739_s_ctrl(struct v4l2_ctrl *ctrl) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = to_sd(ctrl); 878c2ecf20Sopenharmony_ci struct wm8739_state *state = to_state(sd); 888c2ecf20Sopenharmony_ci unsigned int work_l, work_r; 898c2ecf20Sopenharmony_ci u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ 908c2ecf20Sopenharmony_ci u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ 918c2ecf20Sopenharmony_ci u16 mute; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci switch (ctrl->id) { 948c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_VOLUME: 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci default: 988c2ecf20Sopenharmony_ci return -EINVAL; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ 1028c2ecf20Sopenharmony_ci work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768; 1038c2ecf20Sopenharmony_ci work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci vol_l = (long)work_l * 31 / 65535; 1068c2ecf20Sopenharmony_ci vol_r = (long)work_r * 31 / 65535; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* set audio volume etc. */ 1098c2ecf20Sopenharmony_ci mute = state->mute->val ? 0x80 : 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB 1128c2ecf20Sopenharmony_ci * Default setting: 0x17 = 0 dB 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci wm8739_write(sd, R0, (vol_l & 0x1f) | mute); 1158c2ecf20Sopenharmony_ci wm8739_write(sd, R1, (vol_r & 0x1f) | mute); 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct wm8739_state *state = to_state(sd); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci state->clock_freq = audiofreq; 1268c2ecf20Sopenharmony_ci /* de-activate */ 1278c2ecf20Sopenharmony_ci wm8739_write(sd, R9, 0x000); 1288c2ecf20Sopenharmony_ci switch (audiofreq) { 1298c2ecf20Sopenharmony_ci case 44100: 1308c2ecf20Sopenharmony_ci /* 256fps, fs=44.1k */ 1318c2ecf20Sopenharmony_ci wm8739_write(sd, R8, 0x020); 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci case 48000: 1348c2ecf20Sopenharmony_ci /* 256fps, fs=48k */ 1358c2ecf20Sopenharmony_ci wm8739_write(sd, R8, 0x000); 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci case 32000: 1388c2ecf20Sopenharmony_ci /* 256fps, fs=32k */ 1398c2ecf20Sopenharmony_ci wm8739_write(sd, R8, 0x018); 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci default: 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci /* activate */ 1458c2ecf20Sopenharmony_ci wm8739_write(sd, R9, 0x001); 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int wm8739_log_status(struct v4l2_subdev *sd) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct wm8739_state *state = to_state(sd); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); 1548c2ecf20Sopenharmony_ci v4l2_ctrl_handler_log_status(&state->hdl, sd->name); 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops wm8739_ctrl_ops = { 1618c2ecf20Sopenharmony_ci .s_ctrl = wm8739_s_ctrl, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops wm8739_core_ops = { 1658c2ecf20Sopenharmony_ci .log_status = wm8739_log_status, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_audio_ops wm8739_audio_ops = { 1698c2ecf20Sopenharmony_ci .s_clock_freq = wm8739_s_clock_freq, 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops wm8739_ops = { 1738c2ecf20Sopenharmony_ci .core = &wm8739_core_ops, 1748c2ecf20Sopenharmony_ci .audio = &wm8739_audio_ops, 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* i2c implementation */ 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int wm8739_probe(struct i2c_client *client, 1828c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct wm8739_state *state; 1858c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* Check if the adapter supports the needed features */ 1888c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 1898c2ecf20Sopenharmony_ci return -EIO; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci v4l_info(client, "chip found @ 0x%x (%s)\n", 1928c2ecf20Sopenharmony_ci client->addr << 1, client->adapter->name); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); 1958c2ecf20Sopenharmony_ci if (state == NULL) 1968c2ecf20Sopenharmony_ci return -ENOMEM; 1978c2ecf20Sopenharmony_ci sd = &state->sd; 1988c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &wm8739_ops); 1998c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&state->hdl, 2); 2008c2ecf20Sopenharmony_ci state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, 2018c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736); 2028c2ecf20Sopenharmony_ci state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, 2038c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); 2048c2ecf20Sopenharmony_ci state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, 2058c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); 2068c2ecf20Sopenharmony_ci sd->ctrl_handler = &state->hdl; 2078c2ecf20Sopenharmony_ci if (state->hdl.error) { 2088c2ecf20Sopenharmony_ci int err = state->hdl.error; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&state->hdl); 2118c2ecf20Sopenharmony_ci return err; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(3, &state->volume); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci state->clock_freq = 48000; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* Initialize wm8739 */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* reset */ 2208c2ecf20Sopenharmony_ci wm8739_write(sd, R15, 0x00); 2218c2ecf20Sopenharmony_ci /* filter setting, high path, offet clear */ 2228c2ecf20Sopenharmony_ci wm8739_write(sd, R5, 0x000); 2238c2ecf20Sopenharmony_ci /* ADC, OSC, Power Off mode Disable */ 2248c2ecf20Sopenharmony_ci wm8739_write(sd, R6, 0x000); 2258c2ecf20Sopenharmony_ci /* Digital Audio interface format: 2268c2ecf20Sopenharmony_ci Enable Master mode, 24 bit, MSB first/left justified */ 2278c2ecf20Sopenharmony_ci wm8739_write(sd, R7, 0x049); 2288c2ecf20Sopenharmony_ci /* sampling control: normal, 256fs, 48KHz sampling rate */ 2298c2ecf20Sopenharmony_ci wm8739_write(sd, R8, 0x000); 2308c2ecf20Sopenharmony_ci /* activate */ 2318c2ecf20Sopenharmony_ci wm8739_write(sd, R9, 0x001); 2328c2ecf20Sopenharmony_ci /* set volume/mute */ 2338c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&state->hdl); 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int wm8739_remove(struct i2c_client *client) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 2408c2ecf20Sopenharmony_ci struct wm8739_state *state = to_state(sd); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 2438c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&state->hdl); 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic const struct i2c_device_id wm8739_id[] = { 2488c2ecf20Sopenharmony_ci { "wm8739", 0 }, 2498c2ecf20Sopenharmony_ci { } 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wm8739_id); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic struct i2c_driver wm8739_driver = { 2548c2ecf20Sopenharmony_ci .driver = { 2558c2ecf20Sopenharmony_ci .name = "wm8739", 2568c2ecf20Sopenharmony_ci }, 2578c2ecf20Sopenharmony_ci .probe = wm8739_probe, 2588c2ecf20Sopenharmony_ci .remove = wm8739_remove, 2598c2ecf20Sopenharmony_ci .id_table = wm8739_id, 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cimodule_i2c_driver(wm8739_driver); 263