0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/clk.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/kernel.h>
0015 #include <linux/mfd/syscon.h>
0016 #include <linux/module.h>
0017 #include <linux/of.h>
0018 #include <linux/of_platform.h>
0019 #include <linux/platform_device.h>
0020 #include <linux/pm_runtime.h>
0021 #include <linux/timer.h>
0022 #include <linux/workqueue.h>
0023 #include <media/cec.h>
0024 #include <media/cec-notifier.h>
0025
0026 #include "exynos_hdmi_cec.h"
0027 #include "regs-cec.h"
0028 #include "s5p_cec.h"
0029
0030 #define CEC_NAME "s5p-cec"
0031
0032 static int debug;
0033 module_param(debug, int, 0644);
0034 MODULE_PARM_DESC(debug, "debug level (0-2)");
0035
0036 static int s5p_cec_adap_enable(struct cec_adapter *adap, bool enable)
0037 {
0038 int ret;
0039 struct s5p_cec_dev *cec = cec_get_drvdata(adap);
0040
0041 if (enable) {
0042 ret = pm_runtime_resume_and_get(cec->dev);
0043 if (ret < 0)
0044 return ret;
0045
0046 s5p_cec_reset(cec);
0047
0048 s5p_cec_set_divider(cec);
0049 s5p_cec_threshold(cec);
0050
0051 s5p_cec_unmask_tx_interrupts(cec);
0052 s5p_cec_unmask_rx_interrupts(cec);
0053 s5p_cec_enable_rx(cec);
0054 } else {
0055 s5p_cec_mask_tx_interrupts(cec);
0056 s5p_cec_mask_rx_interrupts(cec);
0057 pm_runtime_put(cec->dev);
0058 }
0059
0060 return 0;
0061 }
0062
0063 static int s5p_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
0064 {
0065 struct s5p_cec_dev *cec = cec_get_drvdata(adap);
0066
0067 s5p_cec_set_addr(cec, addr);
0068 return 0;
0069 }
0070
0071 static int s5p_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
0072 u32 signal_free_time, struct cec_msg *msg)
0073 {
0074 struct s5p_cec_dev *cec = cec_get_drvdata(adap);
0075
0076
0077
0078
0079
0080 s5p_cec_copy_packet(cec, msg->msg, msg->len, max(1, attempts - 1));
0081 return 0;
0082 }
0083
0084 static irqreturn_t s5p_cec_irq_handler(int irq, void *priv)
0085 {
0086 struct s5p_cec_dev *cec = priv;
0087 u32 status = 0;
0088
0089 status = s5p_cec_get_status(cec);
0090
0091 dev_dbg(cec->dev, "irq received\n");
0092
0093 if (status & CEC_STATUS_TX_DONE) {
0094 if (status & CEC_STATUS_TX_NACK) {
0095 dev_dbg(cec->dev, "CEC_STATUS_TX_NACK set\n");
0096 cec->tx = STATE_NACK;
0097 } else if (status & CEC_STATUS_TX_ERROR) {
0098 dev_dbg(cec->dev, "CEC_STATUS_TX_ERROR set\n");
0099 cec->tx = STATE_ERROR;
0100 } else {
0101 dev_dbg(cec->dev, "CEC_STATUS_TX_DONE\n");
0102 cec->tx = STATE_DONE;
0103 }
0104 s5p_clr_pending_tx(cec);
0105 }
0106
0107 if (status & CEC_STATUS_RX_DONE) {
0108 if (status & CEC_STATUS_RX_ERROR) {
0109 dev_dbg(cec->dev, "CEC_STATUS_RX_ERROR set\n");
0110 s5p_cec_rx_reset(cec);
0111 s5p_cec_enable_rx(cec);
0112 } else {
0113 dev_dbg(cec->dev, "CEC_STATUS_RX_DONE set\n");
0114 if (cec->rx != STATE_IDLE)
0115 dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n");
0116 cec->rx = STATE_BUSY;
0117 cec->msg.len = status >> 24;
0118 cec->msg.rx_status = CEC_RX_STATUS_OK;
0119 s5p_cec_get_rx_buf(cec, cec->msg.len,
0120 cec->msg.msg);
0121 cec->rx = STATE_DONE;
0122 s5p_cec_enable_rx(cec);
0123 }
0124
0125 s5p_clr_pending_rx(cec);
0126 }
0127 return IRQ_WAKE_THREAD;
0128 }
0129
0130 static irqreturn_t s5p_cec_irq_handler_thread(int irq, void *priv)
0131 {
0132 struct s5p_cec_dev *cec = priv;
0133
0134 dev_dbg(cec->dev, "irq processing thread\n");
0135 switch (cec->tx) {
0136 case STATE_DONE:
0137 cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
0138 cec->tx = STATE_IDLE;
0139 break;
0140 case STATE_NACK:
0141 cec_transmit_done(cec->adap,
0142 CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_NACK,
0143 0, 1, 0, 0);
0144 cec->tx = STATE_IDLE;
0145 break;
0146 case STATE_ERROR:
0147 cec_transmit_done(cec->adap,
0148 CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_ERROR,
0149 0, 0, 0, 1);
0150 cec->tx = STATE_IDLE;
0151 break;
0152 case STATE_BUSY:
0153 dev_err(cec->dev, "state set to busy, this should not occur here\n");
0154 break;
0155 default:
0156 break;
0157 }
0158
0159 switch (cec->rx) {
0160 case STATE_DONE:
0161 cec_received_msg(cec->adap, &cec->msg);
0162 cec->rx = STATE_IDLE;
0163 break;
0164 default:
0165 break;
0166 }
0167
0168 return IRQ_HANDLED;
0169 }
0170
0171 static const struct cec_adap_ops s5p_cec_adap_ops = {
0172 .adap_enable = s5p_cec_adap_enable,
0173 .adap_log_addr = s5p_cec_adap_log_addr,
0174 .adap_transmit = s5p_cec_adap_transmit,
0175 };
0176
0177 static int s5p_cec_probe(struct platform_device *pdev)
0178 {
0179 struct device *dev = &pdev->dev;
0180 struct device *hdmi_dev;
0181 struct s5p_cec_dev *cec;
0182 bool needs_hpd = of_property_read_bool(pdev->dev.of_node, "needs-hpd");
0183 int ret;
0184
0185 hdmi_dev = cec_notifier_parse_hdmi_phandle(dev);
0186
0187 if (IS_ERR(hdmi_dev))
0188 return PTR_ERR(hdmi_dev);
0189
0190 cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
0191 if (!cec)
0192 return -ENOMEM;
0193
0194 cec->dev = dev;
0195
0196 cec->irq = platform_get_irq(pdev, 0);
0197 if (cec->irq < 0)
0198 return cec->irq;
0199
0200 ret = devm_request_threaded_irq(dev, cec->irq, s5p_cec_irq_handler,
0201 s5p_cec_irq_handler_thread, 0, pdev->name, cec);
0202 if (ret)
0203 return ret;
0204
0205 cec->clk = devm_clk_get(dev, "hdmicec");
0206 if (IS_ERR(cec->clk))
0207 return PTR_ERR(cec->clk);
0208
0209 cec->pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
0210 "samsung,syscon-phandle");
0211 if (IS_ERR(cec->pmu))
0212 return -EPROBE_DEFER;
0213
0214 cec->reg = devm_platform_ioremap_resource(pdev, 0);
0215 if (IS_ERR(cec->reg))
0216 return PTR_ERR(cec->reg);
0217
0218 cec->adap = cec_allocate_adapter(&s5p_cec_adap_ops, cec, CEC_NAME,
0219 CEC_CAP_DEFAULTS | (needs_hpd ? CEC_CAP_NEEDS_HPD : 0) |
0220 CEC_CAP_CONNECTOR_INFO, 1);
0221 ret = PTR_ERR_OR_ZERO(cec->adap);
0222 if (ret)
0223 return ret;
0224
0225 cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,
0226 cec->adap);
0227 if (!cec->notifier) {
0228 ret = -ENOMEM;
0229 goto err_delete_adapter;
0230 }
0231
0232 ret = cec_register_adapter(cec->adap, &pdev->dev);
0233 if (ret)
0234 goto err_notifier;
0235
0236 platform_set_drvdata(pdev, cec);
0237 pm_runtime_enable(dev);
0238
0239 dev_dbg(dev, "successfully probed\n");
0240 return 0;
0241
0242 err_notifier:
0243 cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
0244
0245 err_delete_adapter:
0246 cec_delete_adapter(cec->adap);
0247 return ret;
0248 }
0249
0250 static int s5p_cec_remove(struct platform_device *pdev)
0251 {
0252 struct s5p_cec_dev *cec = platform_get_drvdata(pdev);
0253
0254 cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
0255 cec_unregister_adapter(cec->adap);
0256 pm_runtime_disable(&pdev->dev);
0257 return 0;
0258 }
0259
0260 static int __maybe_unused s5p_cec_runtime_suspend(struct device *dev)
0261 {
0262 struct s5p_cec_dev *cec = dev_get_drvdata(dev);
0263
0264 clk_disable_unprepare(cec->clk);
0265 return 0;
0266 }
0267
0268 static int __maybe_unused s5p_cec_runtime_resume(struct device *dev)
0269 {
0270 struct s5p_cec_dev *cec = dev_get_drvdata(dev);
0271 int ret;
0272
0273 ret = clk_prepare_enable(cec->clk);
0274 if (ret < 0)
0275 return ret;
0276 return 0;
0277 }
0278
0279 static const struct dev_pm_ops s5p_cec_pm_ops = {
0280 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
0281 pm_runtime_force_resume)
0282 SET_RUNTIME_PM_OPS(s5p_cec_runtime_suspend, s5p_cec_runtime_resume,
0283 NULL)
0284 };
0285
0286 static const struct of_device_id s5p_cec_match[] = {
0287 {
0288 .compatible = "samsung,s5p-cec",
0289 },
0290 {},
0291 };
0292 MODULE_DEVICE_TABLE(of, s5p_cec_match);
0293
0294 static struct platform_driver s5p_cec_pdrv = {
0295 .probe = s5p_cec_probe,
0296 .remove = s5p_cec_remove,
0297 .driver = {
0298 .name = CEC_NAME,
0299 .of_match_table = s5p_cec_match,
0300 .pm = &s5p_cec_pm_ops,
0301 },
0302 };
0303
0304 module_platform_driver(s5p_cec_pdrv);
0305
0306 MODULE_AUTHOR("Kamil Debski <kamil@wypas.org>");
0307 MODULE_LICENSE("GPL");
0308 MODULE_DESCRIPTION("Samsung S5P CEC driver");