From patchwork Wed Jan 7 13:05:00 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Akihiro TSUKADA X-Patchwork-Id: 27848 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1Y8qIe-0004ay-8A; Wed, 07 Jan 2015 14:05:24 +0100 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.72/mailfrontend-5) with esmtp id 1Y8qIb-0004v4-8Z; Wed, 07 Jan 2015 14:05:23 +0100 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753042AbbAGNFT (ORCPT + 1 other); Wed, 7 Jan 2015 08:05:19 -0500 Received: from mail-pd0-f182.google.com ([209.85.192.182]:36328 "EHLO mail-pd0-f182.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752868AbbAGNFR (ORCPT ); Wed, 7 Jan 2015 08:05:17 -0500 Received: by mail-pd0-f182.google.com with SMTP id p10so4537216pdj.13 for ; Wed, 07 Jan 2015 05:05:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=yl4ZgqUk0pNU3+nUZP8B8IRcmtoEyRW2Ly/IHxV1gMQ=; b=tcc5nMBjsml2Zr+9TgqhgI3YbHQ/vw4ceOXRiIg8cgDYVfKwg6m03MsOboP3TxWnRt 25l/8bRY9W+HasO1EVuWe6SASCutymTdx8jmqrGcbCPRyyLpmpmG1+/jcgIArE2PFLbv un7hI8JWYPljSifqINU8jEPNzN8KnU+RQqRGqbdlAySpqk2fkuQiVkf1UWL67uxP1ptS Zqwzu15eX+l3aq6rZYWtqfnGxk4vtEZY74qRR6wMPCwXQNeVSF6j6LdO1x6Vpx1sAXTu ZpqXgsBENsaZghed2wKKcpsMafuQSwrfPJD+W/vtZaFr0A6113aWQchKPx7xk5ueP6qM N3OA== X-Received: by 10.66.139.167 with SMTP id qz7mr5432945pab.50.1420635916529; Wed, 07 Jan 2015 05:05:16 -0800 (PST) Received: from seabird.localdomain.localdomain (softbank219203027033.bbtec.net. [219.203.27.33]) by mx.google.com with ESMTPSA id ah1sm1849533pad.16.2015.01.07.05.05.13 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 07 Jan 2015 05:05:15 -0800 (PST) From: tskd08@gmail.com To: linux-media@vger.kernel.org Cc: m.chehab@samsung.com, Akihiro Tsukada Subject: [PATCH v2] dvb-core: add template code for i2c binding model Date: Wed, 7 Jan 2015 22:05:00 +0900 Message-Id: <1420635900-32221-1-git-send-email-tskd08@gmail.com> X-Mailer: git-send-email 2.2.1 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 6.0.0.2142326, Antispam-Engine: 2.7.2.2107409, Antispam-Data: 2015.1.7.125420 X-PMX-Spam: Gauge=IIIIIIIII, Probability=9%, Report=' FORGED_FROM_GMAIL 0.1, MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, BODY_SIZE_10000_PLUS 0, DKIM_SIGNATURE 0, DOMAIN_OBFU_DOT 0, NO_REAL_NAME 0, __ANY_URI 0, __CANPHARM_COPYRIGHT 0, __CP_URI_IN_BODY 0, __FRAUD_BODY_WEBMAIL 0, __FRAUD_WEBMAIL 0, __FRAUD_WEBMAIL_FROM 0, __FROM_GMAIL 0, __HAS_FROM 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __MIME_TEXT_ONLY 0, __MULTIPLE_RCPTS_CC_X2 0, __PHISH_SPEAR_STRUCTURE_1 0, __SANE_MSGID 0, __SUBJ_ALPHA_END 0, __TO_MALFORMED_2 0, __TO_NO_NAME 0, __URI_NS , __YOUTUBE_RCVD 0' From: Akihiro Tsukada Changes in v2: - rename some varibles for consistency and readability - (registering of tuner subdev media_entity is NOT implemented yet). Define a standard interface for demod/tuner i2c driver modules. A module client calls dvb_i2c_attach_{fe,tuner}(), and a module driver defines struct dvb_i2c_module_param and calls DEFINE_DVB_I2C_MODULE() macro. This template provides implicit module requests and ref-counting, alloc/free's private data structures, fixes the usage of clientdata of i2c devices, and defines the platformdata structures for passing around device specific config/output parameters between drivers and clients. These kinds of code are common to (almost) all dvb i2c drivers/client, but they were scattered over adapter modules and demod/tuner modules. Signed-off-by: Akihiro Tsukada --- drivers/media/dvb-core/Makefile | 4 + drivers/media/dvb-core/dvb_frontend.h | 1 + drivers/media/dvb-core/dvb_i2c.c | 219 ++++++++++++++++++++++++++++++++++ drivers/media/dvb-core/dvb_i2c.h | 110 +++++++++++++++++ 4 files changed, 334 insertions(+) create mode 100644 drivers/media/dvb-core/dvb_i2c.c create mode 100644 drivers/media/dvb-core/dvb_i2c.h diff --git a/drivers/media/dvb-core/Makefile b/drivers/media/dvb-core/Makefile index 8f22bcd..271648d 100644 --- a/drivers/media/dvb-core/Makefile +++ b/drivers/media/dvb-core/Makefile @@ -8,4 +8,8 @@ dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \ dvb_ca_en50221.o dvb_frontend.o \ $(dvb-net-y) dvb_ringbuffer.o dvb_math.o +ifneq ($(CONFIG_I2C)$(CONFIG_I2C_MODULE),) +dvb-core-objs += dvb_i2c.o +endif + obj-$(CONFIG_DVB_CORE) += dvb-core.o diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 816269e..903719a 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -415,6 +415,7 @@ struct dtv_frontend_properties { struct dvb_frontend { struct dvb_frontend_ops ops; struct dvb_adapter *dvb; + struct i2c_client *fe_i2c_client; void *demodulator_priv; void *tuner_priv; void *frontend_priv; diff --git a/drivers/media/dvb-core/dvb_i2c.c b/drivers/media/dvb-core/dvb_i2c.c new file mode 100644 index 0000000..09c3078 --- /dev/null +++ b/drivers/media/dvb-core/dvb_i2c.c @@ -0,0 +1,219 @@ +/* + * dvb_i2c.c + * + * Copyright 2014 Akihiro Tsukada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include "dvb_i2c.h" + +static struct i2c_client * +dvb_i2c_new_subdev(struct i2c_adapter *adap, struct i2c_board_info *info, + const unsigned short *probe_addrs) +{ + struct i2c_client *cl; + + request_module(I2C_MODULE_PREFIX "%s", info->type); + /* Create the i2c client */ + if (info->addr == 0 && probe_addrs) + cl = i2c_new_probed_device(adap, info, probe_addrs, NULL); + else + cl = i2c_new_device(adap, info); + if (!cl || !cl->dev.driver) + return NULL; + return cl; +} + +struct dvb_frontend * +dvb_i2c_attach_fe(struct i2c_adapter *adap, const struct i2c_board_info *info, + const void *cfg_template, void **out) +{ + struct i2c_client *cl; + struct i2c_board_info tmp_info; + struct dvb_i2c_dev_config cfg; + + cfg.priv_cfg = cfg_template; + cfg.out = out; + tmp_info = *info; + tmp_info.platform_data = &cfg; + + cl = dvb_i2c_new_subdev(adap, &tmp_info, NULL); + if (!cl) + return NULL; + return i2c_get_clientdata(cl); +} +EXPORT_SYMBOL(dvb_i2c_attach_fe); + +struct i2c_client * +dvb_i2c_attach_tuner(struct i2c_adapter *adap, + const struct i2c_board_info *info, + struct dvb_frontend *fe, + const void *cfg_template, void **out) +{ + struct i2c_board_info tmp_info; + struct dvb_i2c_tuner_config cfg; + + cfg.fe = fe; + cfg.devcfg.priv_cfg = cfg_template; + cfg.devcfg.out = out; + tmp_info = *info; + tmp_info.platform_data = &cfg; + + return dvb_i2c_new_subdev(adap, &tmp_info, NULL); +} +EXPORT_SYMBOL(dvb_i2c_attach_tuner); + + +static int +probe_tuner(struct i2c_client *client, const struct i2c_device_id *id, + const struct dvb_i2c_module_param *param, + struct module *this_module) +{ + struct dvb_frontend *fe; + struct dvb_i2c_tuner_config *cfg; + int ret; + + if (!try_module_get(this_module)) + return -ENODEV; + + cfg = client->dev.platform_data; + fe = cfg->fe; + i2c_set_clientdata(client, fe); + + if (param->priv_size > 0) { + fe->tuner_priv = kzalloc(param->priv_size, GFP_KERNEL); + if (!fe->tuner_priv) { + ret = -ENOMEM; + goto err_mem; + } + } + + if (param->ops.tuner_ops) + memcpy(&fe->ops.tuner_ops, param->ops.tuner_ops, + sizeof(fe->ops.tuner_ops)); + + ret = 0; + if (param->priv_probe) + ret = param->priv_probe(client, id); + if (ret != 0) { + dev_info(&client->dev, "driver private probe failed.\n"); + goto err_priv; + } + return 0; + +err_priv: + kfree(fe->tuner_priv); +err_mem: + fe->tuner_priv = NULL; + module_put(this_module); + return ret; +} + +static int remove_tuner(struct i2c_client *client, + const struct dvb_i2c_module_param *param, + struct module *this_module) +{ + struct dvb_frontend *fe; + + if (param->priv_remove) + param->priv_remove(client); + fe = i2c_get_clientdata(client); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + module_put(this_module); + return 0; +} + + +static int probe_fe(struct i2c_client *client, const struct i2c_device_id *id, + const struct dvb_i2c_module_param *param, + struct module *this_module) +{ + struct dvb_frontend *fe; + int ret; + + if (!try_module_get(this_module)) + return -ENODEV; + + fe = kzalloc(sizeof(*fe), GFP_KERNEL); + if (!fe) { + ret = -ENOMEM; + goto err_fe_kfree; + } + i2c_set_clientdata(client, fe); + fe->fe_i2c_client = client; + + if (param->priv_size > 0) { + fe->demodulator_priv = kzalloc(param->priv_size, GFP_KERNEL); + if (!fe->demodulator_priv) { + ret = -ENOMEM; + goto err_fe_kfree; + } + } + + if (param->ops.fe_ops) + memcpy(&fe->ops, param->ops.fe_ops, sizeof(fe->ops)); + + ret = 0; + if (param->priv_probe) + ret = param->priv_probe(client, id); + if (ret != 0) { + dev_info(&client->dev, "driver private probe failed.\n"); + goto err_priv_kfree; + } + return 0; + +err_priv_kfree: + kfree(fe->demodulator_priv); +err_fe_kfree: + kfree(fe); + module_put(this_module); + return ret; +} + +static int remove_fe(struct i2c_client *client, + const struct dvb_i2c_module_param *param, + struct module *this_module) +{ + struct dvb_frontend *fe; + + if (param->priv_remove) + param->priv_remove(client); + fe = i2c_get_clientdata(client); + kfree(fe->demodulator_priv); + kfree(fe); + module_put(this_module); + return 0; +} + + +int dvb_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id, + const struct dvb_i2c_module_param *param, + struct module *this_module) +{ + return param->is_tuner ? probe_tuner(client, id, param, this_module) : + probe_fe(client, id, param, this_module); +} +EXPORT_SYMBOL(dvb_i2c_probe); + +int dvb_i2c_remove(struct i2c_client *client, + const struct dvb_i2c_module_param *param, + struct module *this_module) +{ + return param->is_tuner ? remove_tuner(client, param, this_module) : + remove_fe(client, param, this_module); +} +EXPORT_SYMBOL(dvb_i2c_remove); diff --git a/drivers/media/dvb-core/dvb_i2c.h b/drivers/media/dvb-core/dvb_i2c.h new file mode 100644 index 0000000..4a5365d --- /dev/null +++ b/drivers/media/dvb-core/dvb_i2c.h @@ -0,0 +1,110 @@ +/* + * dvb_i2c.h + * + * Copyright 2014 Akihiro Tsukada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +/* template code for i2c driver modules */ + +#ifndef _DVB_I2C_H_ +#define _DVB_I2C_H_ + +#include +#include +#include + +#include "dvb_frontend.h" + +/* interface for module clients */ + +struct dvb_frontend *dvb_i2c_attach_fe(struct i2c_adapter *adap, + const struct i2c_board_info *info, + const void *cfg_template, void **out); + +struct i2c_client *dvb_i2c_attach_tuner(struct i2c_adapter *adap, + const struct i2c_board_info *info, + struct dvb_frontend *fe, + const void *cfg_template, void **out); + +/* interface for module drivers */ + +/* data structures that are set to i2c_client.dev.platform_data */ + +struct dvb_i2c_dev_config { + const void *priv_cfg; + /* @out [OUT] pointer to per-device data returned from driver */ + void **out; +}; + +struct dvb_i2c_tuner_config { + struct dvb_frontend *fe; + struct dvb_i2c_dev_config devcfg; +}; + +typedef int (*dvb_i2c_probe_func)(struct i2c_client *client, + const struct i2c_device_id *id); +typedef int (*dvb_i2c_remove_func)(struct i2c_client *client); + +struct dvb_i2c_module_param { + union { + const struct dvb_frontend_ops *fe_ops; + const struct dvb_tuner_ops *tuner_ops; + } ops; + /* driver private probe/remove functions */ + dvb_i2c_probe_func priv_probe; + dvb_i2c_remove_func priv_remove; + + u32 priv_size; /* sizeof(device private data) */ + bool is_tuner; +}; + + +int dvb_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id, + const struct dvb_i2c_module_param *param, + struct module *this_module); + +int dvb_i2c_remove(struct i2c_client *client, + const struct dvb_i2c_module_param *param, + struct module *this_module); + + +#define DEFINE_DVB_I2C_MODULE(dname, idtab, param) \ +static int _##dname##_probe(struct i2c_client *client,\ + const struct i2c_device_id *id)\ +{\ + return dvb_i2c_probe(client, id, &(param), THIS_MODULE);\ +} \ +\ +static int _##dname##_remove(struct i2c_client *client)\ +{\ + return dvb_i2c_remove(client, &(param), THIS_MODULE);\ +} \ +\ +MODULE_DEVICE_TABLE(i2c, idtab);\ +\ +static struct i2c_driver dname##_driver = {\ + .driver = {\ + .name = #dname,\ + },\ + .probe = _##dname##_probe,\ + .remove = _##dname##_remove,\ + .id_table = idtab,\ +};\ +\ +module_i2c_driver(dname##_driver) + +#endif /* _DVB_I2C_H */