[COMMON] soc: samsung: Add the bcm driver
authorSeokju Yoon <sukju.yoon@samsung.com>
Mon, 20 Jun 2016 02:19:09 +0000 (11:19 +0900)
committerTaekki Kim <taekki.kim@samsung.com>
Mon, 14 May 2018 05:42:48 +0000 (14:42 +0900)
Change-Id: Ibe3ce4189b269d21fb9781160b6c119dcdb82e07
Signed-off-by: Seokju Yoon <sukju.yoon@samsung.com>
drivers/soc/samsung/exynos-bcm.c [new file with mode: 0644]
include/soc/samsung/bcm.h [new file with mode: 0644]

diff --git a/drivers/soc/samsung/exynos-bcm.c b/drivers/soc/samsung/exynos-bcm.c
new file mode 100644 (file)
index 0000000..0b663d5
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/exynos_ion.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+#include <asm/cacheflush.h>
+#include <soc/samsung/exynos-pd.h>
+#include <linux/suspend.h>
+
+#include <soc/samsung/cal-if.h>
+#include <soc/samsung/bcm.h>
+#include <linux/memblock.h>
+#include <asm/map.h>
+
+#define BCM_BDGGEN
+#ifdef BCM_BDGGEN
+#define BCM_BDG(x...) pr_info("bcm: " x)
+#else
+#define BCM_BDG(x...) do {} while (0)
+#endif
+
+#define BCM_MAX_DATA           4 * 1024 * 1024 / 32
+#define MAX_STR                        4 * 1024
+#define BCM_SIZE                       SZ_64K
+#define NUM_CLK_MAX            8
+#define FILE_STR               32
+
+enum outform {
+       OUT_FILE = 1,
+       OUT_LOG,
+};
+
+static size_t fd_virt_addr;
+
+struct bcm_info {
+       char *pd_name;
+       int on;
+       char *clk_name[NUM_CLK_MAX];
+       struct clk *clk[NUM_CLK_MAX];
+};
+
+static struct fw_system_func {
+       int (*fw_show)(char *);
+       char * (*fw_cmd)(const char *);
+       struct output_data *(*fw_init)(const int *);
+       struct output_data *(*fw_stop)(u64, unsigned long (*func)(unsigned int),const int *);
+       int (*fw_periodic)(u64, unsigned long (*func)(unsigned int), const int *);
+       char *(*fw_getresult)(char *str);
+       int (*fw_exit)(void);
+       int (*pd_sync)(struct bcm_info *, int, u64);
+       struct bcm_info *(*get_pd)(void);
+       int (*get_outform)(void);
+} *fw_func;
+
+static struct os_system_func {
+       struct output_data *fdata;
+       struct output_data *ldata;
+       void __iomem *(*remap)(phys_addr_t phys_addr, size_t size);
+       void (*unmap)(volatile void __iomem *addr);
+       int (*sprint)(char *buf, const char *fmt, ...);
+       int (*print)(const char *, ...);
+} os_func;
+
+static DEFINE_SPINLOCK(bcm_lock);
+static char input_file[FILE_STR] = "/data/bcm.bin";
+
+static struct hrtimer bcm_hrtimer;
+static struct workqueue_struct *bcm_wq;
+static struct bcm_work_struct {
+       struct work_struct work;
+       char *data;
+} *work_file_out;
+
+static void write_file(struct work_struct *work)
+{
+       char *result;
+       char *filename;
+       unsigned long flags;
+       struct file *fp = NULL;
+       mm_segment_t old_fs = get_fs();
+
+       result = kzalloc(sizeof(char) * MAX_STR, GFP_KERNEL);
+       if (!result) {
+               pr_err("result memory allocation fail!\n");
+               goto err_alloc;
+       }
+
+       spin_lock_irqsave(&bcm_lock, flags);
+       if (fw_func)
+               filename = fw_func->fw_getresult(result);
+       spin_unlock_irqrestore(&bcm_lock, flags);
+       if (!fw_func || !filename) {
+               goto err_firm;
+       }
+
+       set_fs(KERNEL_DS);
+
+       fp = filp_open(filename, O_WRONLY|O_CREAT|O_APPEND, 0);
+       if (IS_ERR(fp)) {
+               pr_err("name : %s filp_open fail!!\n", filename);
+               goto err_filp_open;
+
+       }
+       do {
+               if (result)
+                       vfs_write(fp, result, strlen(result), &fp->f_pos);
+               spin_lock_irqsave(&bcm_lock, flags);
+               filename = fw_func->fw_getresult(result);
+               spin_unlock_irqrestore(&bcm_lock, flags);
+       } while(filename);
+
+       filp_close(fp, NULL);
+err_filp_open:
+       set_fs(old_fs);
+err_firm:
+       kfree(result);
+err_alloc:
+       return;
+}
+
+static void bcm_file_out (char *data)
+{
+       work_file_out->data = data;
+
+       queue_work(bcm_wq, (struct work_struct *)work_file_out);
+}
+
+static u64 get_time(void)
+{
+       return sched_clock();
+}
+
+int bcm_pd_sync(struct bcm_info *bcm, bool on)
+{
+       int ret = 0;
+       unsigned long flags;
+       int i;
+
+       if (on ^ bcm->on) {
+               if (on) {
+                       for (i = 0; bcm->clk[i] && i < NUM_CLK_MAX; i++)
+                               clk_enable(bcm->clk[i]);
+               }
+               spin_lock_irqsave(&bcm_lock, flags);
+               ret = fw_func->pd_sync(bcm, on, get_time());
+               spin_unlock_irqrestore(&bcm_lock, flags);
+
+               if (!on) {
+                       for (i = 0; bcm->clk[i] && i < NUM_CLK_MAX; i++)
+                               clk_disable(bcm->clk[i]);
+               }
+       }
+       return ret;
+}
+EXPORT_SYMBOL(bcm_pd_sync);
+
+static enum hrtimer_restart monitor_fn(struct hrtimer *hrtimer)
+{
+       unsigned long flags;
+       int duration;
+       enum hrtimer_restart ret = HRTIMER_NORESTART;
+
+       spin_lock_irqsave(&bcm_lock, flags);
+       duration = fw_func->fw_periodic(get_time(),
+                       cal_dfs_cached_get_rate, NULL);
+       spin_unlock_irqrestore(&bcm_lock, flags);
+
+       if (duration > 0) {
+               hrtimer_forward_now(hrtimer, ns_to_ktime(duration *
+                                                        NSEC_PER_USEC));
+               ret = HRTIMER_RESTART;
+       }
+       return ret;
+}
+
+struct output_data *bcm_start(const int *usr)
+{
+       unsigned long flags;
+       struct output_data *data = NULL;
+       int duration = 0;
+       if (fw_func) {
+               spin_lock_irqsave(&bcm_lock, flags);
+               data = fw_func->fw_init(usr);
+               if (data) {
+                       duration = fw_func->fw_periodic(get_time(),
+                                       cal_dfs_cached_get_rate, usr);
+               }
+               spin_unlock_irqrestore(&bcm_lock, flags);
+               if (duration > 0) {
+                       if (bcm_hrtimer.state)
+                               hrtimer_try_to_cancel(&bcm_hrtimer);
+                       if (!bcm_hrtimer.state)
+                               hrtimer_start(&bcm_hrtimer,
+                                               ns_to_ktime(duration *
+                                                           NSEC_PER_USEC),
+                                               HRTIMER_MODE_REL);
+               }
+       }
+       return data;
+}
+EXPORT_SYMBOL(bcm_start);
+
+static int bcm_log(void)
+{
+       unsigned long flags;
+       char *str;
+       int err;
+       str = kzalloc(sizeof(char) * MAX_STR, GFP_KERNEL);
+       if (!str) {
+               err = -ENOMEM;
+               return err;
+       }
+       spin_lock_irqsave(&bcm_lock, flags);
+       while (fw_func->fw_getresult(str)) {
+               spin_unlock_irqrestore(&bcm_lock, flags);
+               pr_info("%s", str);
+               spin_lock_irqsave(&bcm_lock, flags);
+       }
+       spin_unlock_irqrestore(&bcm_lock, flags);
+       kfree(str);
+       return 0;
+}
+
+struct output_data *bcm_stop(const int *usr)
+{
+       unsigned long flags;
+       struct output_data *data = NULL;
+       if (fw_func) {
+               spin_lock_irqsave(&bcm_lock, flags);
+               data = fw_func->fw_stop(get_time(),
+                               cal_dfs_cached_get_rate, usr);
+               spin_unlock_irqrestore(&bcm_lock, flags);
+               if (data) {
+                       hrtimer_try_to_cancel(&bcm_hrtimer);
+                       switch (fw_func->get_outform()) {
+                       case OUT_FILE:
+                               bcm_file_out(NULL);
+                               break;
+                       case OUT_LOG:
+                               bcm_log();
+                               break;
+                       }
+               }
+       }
+       return data;
+}
+EXPORT_SYMBOL(bcm_stop);
+
+static void __iomem *bcm_ioremap(phys_addr_t phys_addr, size_t size)
+{
+       void __iomem *ret;
+       ret =  ioremap(phys_addr, size);
+       if (!ret)
+               pr_err("failed to map bcm physical address\n");
+       return ret;
+}
+
+typedef struct fw_system_func*(*start_up_func_t)(void **func);
+
+static void pd_init(void)
+{
+       struct exynos_pm_domain *exypd = NULL;
+       struct bcm_info *bcm;
+       int i;
+       while (bcm = fw_func->get_pd(), bcm) {
+               for (i = 0; i < NUM_CLK_MAX; i++){
+                       if (bcm->clk_name[i]) {
+                               bcm->clk[i] = clk_get(NULL, bcm->clk_name[i]);
+                               if (IS_ERR(bcm->clk[i]))
+                                       pr_err("failed to get clk %s\n",
+                                               bcm->clk_name[i]);
+                               else
+                                       clk_prepare(bcm->clk[i]);
+                       } else {
+                               bcm->clk[i] = NULL;
+                               break;
+                       }
+               }
+               exypd = NULL;
+               bcm->on = false;
+               exypd = exynos_pd_lookup_name(bcm->pd_name);
+               if (exypd) {
+                       mutex_lock(&exypd->access_lock);
+                       exypd->bcm = bcm;
+                       if (cal_pd_status(exypd->cal_pdid)) {
+                               bcm_pd_sync(bcm, true);
+                       }
+                       mutex_unlock(&exypd->access_lock);
+               } else {
+                       bcm_pd_sync(bcm, true);
+               }
+       }
+}
+
+static int load_bcm_bin(struct work_struct *work)
+{
+       int ret = 0;
+       struct file *fp = NULL;
+       long fsize, nread;
+       unsigned long flags;
+       u8 *buf = NULL;
+       char *lib_isp = NULL;
+       mm_segment_t old_fs;
+
+       os_func.print = printk;
+       os_func.sprint = sprintf;
+       os_func.remap = bcm_ioremap;
+       os_func.unmap = iounmap;
+
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+       fp = filp_open(input_file, O_RDONLY, 0);
+       if (IS_ERR(fp)) {
+               pr_err("name : %s filp_open fail!!\n", input_file);
+               ret = -EIO;
+               goto err_fopen;
+       }
+
+       fsize = fp->f_path.dentry->d_inode->i_size;
+       BCM_BDG("start, file path %s, size %ld Bytes\n",
+               input_file, fsize);
+       buf = vmalloc(fsize);
+       if (!buf) {
+               pr_err("failed to allocate memory\n");
+               ret = -ENOMEM;
+               goto err_alloc;
+
+       }
+
+       nread = vfs_read(fp, (char __user *)buf, fsize, &fp->f_pos);
+       if (nread != fsize) {
+               pr_err("failed to read firmware file, %ld Bytes\n", nread);
+               ret = -EIO;
+               goto err_vfs_read;
+       }
+
+       lib_isp = (char *)fd_virt_addr;
+       /* TODO: Must change below size of reserved memory */
+       memset((char *)fd_virt_addr, 0x0, BCM_SIZE);
+
+       spin_lock_irqsave(&bcm_lock, flags);
+       flush_icache_range((unsigned long)lib_isp,
+                          (unsigned long)lib_isp + BCM_SIZE);
+       memcpy((void *)lib_isp, (void *)buf, fsize);
+       flush_cache_all();
+
+       spin_unlock_irqrestore(&bcm_lock, flags);
+       fw_func = ((start_up_func_t)lib_isp)((void **)&os_func);
+       pd_init();
+
+err_vfs_read:
+       vfree((void *)buf);
+err_alloc:
+       filp_close(fp, NULL);
+err_fopen:
+       set_fs(old_fs);
+       return ret;
+}
+
+static void pd_exit(void)
+{
+       struct bcm_info *bcm;
+       struct exynos_pm_domain *exypd = NULL;
+       int i;
+       while (bcm = fw_func->get_pd(), bcm) {
+               exypd = exynos_pd_lookup_name(bcm->pd_name);
+               if (exypd) {
+                       mutex_lock(&exypd->access_lock);
+                       exypd->bcm = NULL;
+                       bcm_pd_sync(bcm, false);
+                       mutex_unlock(&exypd->access_lock);
+               } else {
+                       bcm_pd_sync(bcm, false);
+               }
+               if (bcm->on) {
+                       for (i = 0; bcm->clk[i] && i < NUM_CLK_MAX; i++)
+                               clk_disable_unprepare(bcm->clk[i]);
+               } else {
+                       for (i = 0; bcm->clk[i] && i < NUM_CLK_MAX; i++)
+                               clk_unprepare(bcm->clk[i]);
+               }
+       }
+}
+
+static ssize_t store_load_bcm_fw(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       unsigned long flags;
+       int ret;
+       bool value = true;
+       char str[FILE_STR];
+
+       ret = sscanf(buf, "%s", str);
+       if (ret != 1) {
+               dev_err(dev, "failed sscanf %d\n", ret);
+               return -EINVAL;
+       }
+       if (str[0] == '0')
+               value = false;
+       else if (str[0] == '/')
+               strncpy(input_file, str, strlen(str) + 1);
+
+       /* bcm stop and pd unprepare */
+       if (fw_func) {
+               pd_exit();
+               spin_lock_irqsave(&bcm_lock, flags);
+               if (fw_func->fw_stop(0, NULL, NULL))
+                       hrtimer_try_to_cancel(&bcm_hrtimer);
+               fw_func->fw_cmd("0");
+               spin_unlock_irqrestore(&bcm_lock, flags);
+               fw_func->fw_exit();
+               fw_func = NULL;
+       }
+       if (value) {
+               if (!os_func.fdata) {
+                       os_func.fdata = kzalloc(sizeof(struct output_data) *
+                                               BCM_MAX_DATA, GFP_KERNEL);
+                       os_func.ldata = os_func.fdata + BCM_MAX_DATA;
+               } else {
+                       memset((char *)os_func.fdata, 0x0,
+                              sizeof(struct output_data) * BCM_MAX_DATA);
+               }
+
+               /* load binary */
+               ret = load_bcm_bin(NULL);
+               if (!ret)
+                       return count;
+       }
+
+       kfree(os_func.fdata);
+       os_func.fdata = NULL;
+       os_func.ldata = NULL;
+       return count;
+}
+
+static ssize_t show_load_bcm_fw(struct device *dev,
+                                       struct device_attribute *attr, char *buf)
+{
+       ssize_t count = 0;
+
+       count += snprintf(buf, PAGE_SIZE, "[BCM] addr %llx size %d: ",
+                         (u64)fd_virt_addr, BCM_SIZE);
+       if(fw_func) {
+               count += snprintf(buf + count, PAGE_SIZE, "%s done\n",
+                                 input_file);
+       } else {
+               count += snprintf(buf + count, PAGE_SIZE, "%s not yet\n",
+                                 input_file);
+       }
+       return count;
+}
+
+#define BCM_START 1
+#define BCM_STOP 0
+
+static ssize_t store_cmd_bcm_fw(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       char * info_str = NULL;
+       int cmd;
+       int option = 1;
+       int ret = 0;
+       unsigned long flags;
+
+       if (fw_func) {
+               ret = sscanf(buf, "%d %d", &cmd, &option);
+               switch (cmd) {
+               case BCM_STOP:
+                       spin_lock_irqsave(&bcm_lock, flags);
+                       if (fw_func->fw_stop(get_time(),
+                                       cal_dfs_cached_get_rate, NULL))
+                               hrtimer_try_to_cancel(&bcm_hrtimer);
+                       info_str = fw_func->fw_cmd(buf);
+                       spin_unlock_irqrestore(&bcm_lock, flags);
+                       switch (fw_func->get_outform()) {
+                       case OUT_LOG:
+                               bcm_log();
+                               break;
+                       default:
+                               write_file(NULL);
+                       }
+                       break;
+               case BCM_START:
+                       spin_lock_irqsave(&bcm_lock, flags);
+                       info_str = fw_func->fw_cmd(buf);
+                       spin_unlock_irqrestore(&bcm_lock, flags);
+                       if (info_str && option)
+                               bcm_start(NULL);
+                       break;
+               default:
+                       pd_exit();
+                       spin_lock_irqsave(&bcm_lock, flags);
+                       if (fw_func->fw_stop(get_time(),
+                                       cal_dfs_cached_get_rate, NULL))
+                               hrtimer_try_to_cancel(&bcm_hrtimer);
+                       info_str = fw_func->fw_cmd(buf);
+                       spin_unlock_irqrestore(&bcm_lock, flags);
+                       pd_init();
+                       break;
+               }
+
+               if (info_str)
+                       BCM_BDG ("command: %s\n", info_str);
+       } else {
+               BCM_BDG ("bcm binary is not loaded!\n");
+       }
+
+       return count;
+}
+
+static ssize_t show_cmd_bcm_fw(struct device *dev,
+                                       struct device_attribute *attr, char *buf)
+{
+       ssize_t count = 0;
+       unsigned long flags;
+
+       if (fw_func) {
+               spin_lock_irqsave(&bcm_lock, flags);
+
+               if (fw_func->fw_show)
+                       count += fw_func->fw_show(buf);
+
+               spin_unlock_irqrestore(&bcm_lock, flags);
+       } else {
+               BCM_BDG ("bcm binary is not loaded!\n");
+       }
+       return count;
+}
+static DEVICE_ATTR(load_bin, 0640, show_load_bcm_fw, store_load_bcm_fw);
+static DEVICE_ATTR(command, 0640, show_cmd_bcm_fw, store_cmd_bcm_fw);
+
+static struct attribute *bcm_sysfs_entries[] = {
+       &dev_attr_load_bin.attr,
+       &dev_attr_command.attr,
+       NULL,
+};
+
+static struct attribute_group bcm_attr_group = {
+       .attrs  = bcm_sysfs_entries,
+};
+
+static int exynos_bcm_notifier_event(struct notifier_block *this,
+               unsigned long event,
+               void *ptr)
+{
+       unsigned long flags;
+
+       if (fw_func) {
+               switch ((unsigned int)event) {
+               case PM_POST_SUSPEND:
+                       bcm_start(NULL);
+                       return NOTIFY_OK;
+               case PM_SUSPEND_PREPARE:
+                       spin_lock_irqsave(&bcm_lock, flags);
+                       if (fw_func->fw_stop(get_time(),
+                                               cal_dfs_cached_get_rate, NULL))
+                               hrtimer_try_to_cancel(&bcm_hrtimer);
+                       fw_func->fw_cmd(NULL);
+                       spin_unlock_irqrestore(&bcm_lock, flags);
+                       return NOTIFY_OK;
+               }
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block exynos_bcm_notifier = {
+       .notifier_call = exynos_bcm_notifier_event,
+};
+
+static int bcm_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       ret = sysfs_create_group(&pdev->dev.kobj, &bcm_attr_group);
+       if (ret) {
+               dev_err(&pdev->dev, "failed create sysfs for sci debug data\n");
+               goto err_sysfs;
+       }
+
+       bcm_wq = create_freezable_workqueue("bcm_wq");
+       if (IS_ERR(bcm_wq)) {
+               pr_err("%s: couldn't create workqueue\n", __FILE__);
+               goto err_workqueue;
+       }
+
+       work_file_out = (struct bcm_work_struct *)
+               devm_kzalloc(&pdev->dev, sizeof(struct bcm_work_struct),
+                            GFP_KERNEL);
+       if(!work_file_out) {
+               goto err_file_out;
+       }
+       INIT_WORK((struct work_struct *)work_file_out, write_file);
+
+       register_pm_notifier(&exynos_bcm_notifier);
+
+       hrtimer_init(&bcm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       bcm_hrtimer.function = &monitor_fn;
+
+       BCM_BDG("bcm driver is probed\n");
+
+       return 0;
+
+err_file_out:
+       destroy_workqueue(bcm_wq);
+err_workqueue:
+       sysfs_remove_group(&pdev->dev.kobj, &bcm_attr_group);
+err_sysfs:
+       return 0;
+}
+
+static int bcm_remove(struct platform_device *pdev)
+{
+       unregister_pm_notifier(&exynos_bcm_notifier);
+       destroy_workqueue(bcm_wq);
+       sysfs_remove_group(&pdev->dev.kobj, &bcm_attr_group);
+       return 0;
+}
+
+static const struct of_device_id bcm_dt_match[] = {
+       {
+               .compatible = "samsung,bcm",
+       },
+       {},
+};
+
+static struct platform_driver bcm_driver = {
+       .probe          = bcm_probe,
+       .remove         = bcm_remove,
+       .driver         = {
+               .name   = "bcm",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static struct platform_device bcm_device = {
+       .name   = "bcm",
+       .id     = -1,
+};
+
+static int __init bcm_setup(char *str)
+{
+       struct map_desc fd_table;
+       phys_addr_t fd_phys_addr;
+       if (kstrtoul(str, 0, (unsigned long *)&fd_virt_addr))
+               goto out;
+       fd_phys_addr = memblock_alloc(BCM_SIZE, SZ_4K);
+       fd_table.virtual = (ulong)fd_virt_addr;
+       fd_table.pfn = __phys_to_pfn(fd_phys_addr);
+       fd_table.length = BCM_SIZE;
+       fd_table.type = MT_MEMORY;
+
+       iotable_init_exec(&fd_table, 1);
+       return 0;
+out:
+       return -1;
+}
+__setup("bcm_setup=", bcm_setup);
+
+static int __init bcm_drv_register(void)
+{
+       int ret;
+       BCM_BDG("%s: bcm init\n", __func__);
+
+       if (!fd_virt_addr)
+               return -EINVAL;
+
+       ret = platform_device_register(&bcm_device );
+       if (ret)
+               return ret;
+
+       return platform_driver_register(&bcm_driver);
+}
+late_initcall(bcm_drv_register);
+
+MODULE_AUTHOR("Seokju Yoon <sukju.yoon@samsung.com>");
+MODULE_DESCRIPTION("Samsung BCM driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("interface:bcm");
diff --git a/include/soc/samsung/bcm.h b/include/soc/samsung/bcm.h
new file mode 100644 (file)
index 0000000..a4b6145
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * 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.
+ */
+
+#ifndef __EXYNOS_BCM_H_
+#define __EXYNOS_BCM_H_
+
+struct bcm_info;
+
+struct output_data {
+       int index;
+       u32 rd0;
+       u32 rd1;
+       u32 rd2;
+       u32 rd3;
+       u32 rd4;
+       u64 rd5;
+};
+
+
+#if defined(CONFIG_EXYNOS_BCM)
+int bcm_pd_sync(struct bcm_info *, bool);
+struct output_data *bcm_start(const int *);
+struct output_data *bcm_stop(const int *);
+#else
+
+#define bcm_pd_sync(a, b) do {} while (0)
+#define bcm_start(a) do {} while (0)
+#define bcm_stop(a) do {} while (0)
+#endif
+
+#endif