| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Designware HDMI CEC driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2015-2017 Russell King. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 8 | | - * published by the Free Software Foundation. |
|---|
| 9 | 6 | */ |
|---|
| 7 | +#include <linux/input.h> |
|---|
| 10 | 8 | #include <linux/interrupt.h> |
|---|
| 11 | 9 | #include <linux/io.h> |
|---|
| 10 | +#include <linux/miscdevice.h> |
|---|
| 12 | 11 | #include <linux/module.h> |
|---|
| 13 | 12 | #include <linux/platform_device.h> |
|---|
| 14 | 13 | #include <linux/sched.h> |
|---|
| 15 | 14 | #include <linux/slab.h> |
|---|
| 16 | 15 | |
|---|
| 17 | 16 | #include <drm/drm_edid.h> |
|---|
| 17 | +#include <drm/bridge/dw_hdmi.h> |
|---|
| 18 | 18 | |
|---|
| 19 | 19 | #include <media/cec.h> |
|---|
| 20 | 20 | #include <media/cec-notifier.h> |
|---|
| .. | .. |
|---|
| 24 | 24 | enum { |
|---|
| 25 | 25 | HDMI_IH_CEC_STAT0 = 0x0106, |
|---|
| 26 | 26 | HDMI_IH_MUTE_CEC_STAT0 = 0x0186, |
|---|
| 27 | + HDMI_IH_MUTE = 0x01ff, |
|---|
| 27 | 28 | |
|---|
| 28 | 29 | HDMI_CEC_CTRL = 0x7d00, |
|---|
| 30 | + CEC_TRANS_MASK = 0x7, |
|---|
| 31 | + CEC_CTRL_STANDBY = BIT(4), |
|---|
| 29 | 32 | CEC_CTRL_START = BIT(0), |
|---|
| 30 | 33 | CEC_CTRL_FRAME_TYP = 3 << 1, |
|---|
| 31 | 34 | CEC_CTRL_RETRY = 0 << 1, |
|---|
| .. | .. |
|---|
| 50 | 53 | HDMI_CEC_RX_CNT = 0x7d08, |
|---|
| 51 | 54 | HDMI_CEC_TX_DATA0 = 0x7d10, |
|---|
| 52 | 55 | HDMI_CEC_RX_DATA0 = 0x7d20, |
|---|
| 56 | + HDMI_CEC_RX_DATA1 = 0x7d21, |
|---|
| 53 | 57 | HDMI_CEC_LOCK = 0x7d30, |
|---|
| 54 | 58 | HDMI_CEC_WKUPCTRL = 0x7d31, |
|---|
| 55 | 59 | }; |
|---|
| 56 | 60 | |
|---|
| 57 | 61 | struct dw_hdmi_cec { |
|---|
| 62 | + struct device *dev; |
|---|
| 58 | 63 | struct dw_hdmi *hdmi; |
|---|
| 64 | + struct miscdevice misc_dev; |
|---|
| 59 | 65 | const struct dw_hdmi_cec_ops *ops; |
|---|
| 60 | 66 | u32 addresses; |
|---|
| 61 | 67 | struct cec_adapter *adap; |
|---|
| .. | .. |
|---|
| 64 | 70 | bool tx_done; |
|---|
| 65 | 71 | bool rx_done; |
|---|
| 66 | 72 | struct cec_notifier *notify; |
|---|
| 73 | + struct input_dev *devinput; |
|---|
| 67 | 74 | int irq; |
|---|
| 75 | + int wake_irq; |
|---|
| 76 | + bool wake_en; |
|---|
| 77 | + bool standby_en; |
|---|
| 78 | + struct mutex wake_lock; |
|---|
| 68 | 79 | }; |
|---|
| 69 | 80 | |
|---|
| 70 | 81 | static void dw_hdmi_write(struct dw_hdmi_cec *cec, u8 val, int offset) |
|---|
| .. | .. |
|---|
| 75 | 86 | static u8 dw_hdmi_read(struct dw_hdmi_cec *cec, int offset) |
|---|
| 76 | 87 | { |
|---|
| 77 | 88 | return cec->ops->read(cec->hdmi, offset); |
|---|
| 89 | +} |
|---|
| 90 | + |
|---|
| 91 | +static void dw_hdmi_mod(struct dw_hdmi_cec *cec, unsigned int offset, u8 mask, u8 val) |
|---|
| 92 | +{ |
|---|
| 93 | + cec->ops->mod(cec->hdmi, val, mask, offset); |
|---|
| 78 | 94 | } |
|---|
| 79 | 95 | |
|---|
| 80 | 96 | static int dw_hdmi_cec_log_addr(struct cec_adapter *adap, u8 logical_addr) |
|---|
| .. | .. |
|---|
| 115 | 131 | dw_hdmi_write(cec, msg->msg[i], HDMI_CEC_TX_DATA0 + i); |
|---|
| 116 | 132 | |
|---|
| 117 | 133 | dw_hdmi_write(cec, msg->len, HDMI_CEC_TX_CNT); |
|---|
| 118 | | - dw_hdmi_write(cec, ctrl | CEC_CTRL_START, HDMI_CEC_CTRL); |
|---|
| 134 | + dw_hdmi_mod(cec, HDMI_CEC_CTRL, CEC_TRANS_MASK, ctrl | CEC_CTRL_START); |
|---|
| 119 | 135 | |
|---|
| 120 | 136 | return 0; |
|---|
| 121 | 137 | } |
|---|
| .. | .. |
|---|
| 191 | 207 | struct dw_hdmi_cec *cec = cec_get_drvdata(adap); |
|---|
| 192 | 208 | |
|---|
| 193 | 209 | if (!enable) { |
|---|
| 194 | | - dw_hdmi_write(cec, ~0, HDMI_CEC_MASK); |
|---|
| 195 | | - dw_hdmi_write(cec, ~0, HDMI_IH_MUTE_CEC_STAT0); |
|---|
| 196 | 210 | dw_hdmi_write(cec, 0, HDMI_CEC_POLARITY); |
|---|
| 197 | 211 | |
|---|
| 198 | | - cec->ops->disable(cec->hdmi); |
|---|
| 212 | + if (cec->wake_en && cec->standby_en) { |
|---|
| 213 | + dw_hdmi_write(cec, 0xff, HDMI_IH_CEC_STAT0); |
|---|
| 214 | + dw_hdmi_mod(cec, HDMI_CEC_CTRL, CEC_CTRL_STANDBY, CEC_CTRL_STANDBY); |
|---|
| 215 | + dw_hdmi_write(cec, 0, HDMI_CEC_LOCK); |
|---|
| 216 | + dw_hdmi_write(cec, 0xff, HDMI_CEC_WKUPCTRL); |
|---|
| 217 | + dw_hdmi_write(cec, ~(1 << 6), HDMI_CEC_MASK); |
|---|
| 218 | + dw_hdmi_write(cec, ~(1 << 6), HDMI_IH_MUTE_CEC_STAT0); |
|---|
| 219 | + dw_hdmi_write(cec, 0x01, HDMI_IH_MUTE); |
|---|
| 220 | + } else { |
|---|
| 221 | + cec->ops->disable(cec->hdmi); |
|---|
| 222 | + } |
|---|
| 199 | 223 | } else { |
|---|
| 200 | 224 | unsigned int irqs; |
|---|
| 201 | 225 | |
|---|
| 202 | | - dw_hdmi_write(cec, 0, HDMI_CEC_CTRL); |
|---|
| 226 | + dw_hdmi_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID); |
|---|
| 227 | + dw_hdmi_mod(cec, HDMI_CEC_CTRL, CEC_CTRL_STANDBY, 0); |
|---|
| 228 | + dw_hdmi_write(cec, 0x02, HDMI_IH_MUTE); |
|---|
| 203 | 229 | dw_hdmi_write(cec, ~0, HDMI_IH_CEC_STAT0); |
|---|
| 204 | 230 | dw_hdmi_write(cec, 0, HDMI_CEC_LOCK); |
|---|
| 205 | | - |
|---|
| 206 | | - dw_hdmi_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID); |
|---|
| 207 | 231 | |
|---|
| 208 | 232 | cec->ops->enable(cec->hdmi); |
|---|
| 209 | 233 | |
|---|
| .. | .. |
|---|
| 229 | 253 | cec_delete_adapter(cec->adap); |
|---|
| 230 | 254 | } |
|---|
| 231 | 255 | |
|---|
| 256 | +static irqreturn_t dw_hdmi_cec_wake_irq(int irq, void *data) |
|---|
| 257 | +{ |
|---|
| 258 | + struct cec_adapter *adap = data; |
|---|
| 259 | + struct dw_hdmi_cec *cec = cec_get_drvdata(adap); |
|---|
| 260 | + u8 cec_int; |
|---|
| 261 | + |
|---|
| 262 | + cec_int = dw_hdmi_read(cec, HDMI_IH_CEC_STAT0); |
|---|
| 263 | + if (!cec_int) |
|---|
| 264 | + return IRQ_NONE; |
|---|
| 265 | + |
|---|
| 266 | + dw_hdmi_write(cec, 0x02, HDMI_IH_MUTE); |
|---|
| 267 | + dw_hdmi_write(cec, cec_int, HDMI_IH_CEC_STAT0); |
|---|
| 268 | + dw_hdmi_write(cec, 0x00, HDMI_CEC_WKUPCTRL); |
|---|
| 269 | + |
|---|
| 270 | + if (!cec->wake_en) |
|---|
| 271 | + return IRQ_HANDLED; |
|---|
| 272 | + |
|---|
| 273 | + return IRQ_WAKE_THREAD; |
|---|
| 274 | +} |
|---|
| 275 | + |
|---|
| 276 | +static irqreturn_t dw_hdmi_cec_wake_thread(int irq, void *data) |
|---|
| 277 | +{ |
|---|
| 278 | + struct cec_adapter *adap = data; |
|---|
| 279 | + struct dw_hdmi_cec *cec = cec_get_drvdata(adap); |
|---|
| 280 | + |
|---|
| 281 | + mutex_lock(&cec->wake_lock); |
|---|
| 282 | + |
|---|
| 283 | + if (!cec->standby_en) { |
|---|
| 284 | + mutex_unlock(&cec->wake_lock); |
|---|
| 285 | + return IRQ_HANDLED; |
|---|
| 286 | + } |
|---|
| 287 | + cec->standby_en = false; |
|---|
| 288 | + |
|---|
| 289 | + dev_dbg(cec->dev, "wakeup opcode:0x%x\n", dw_hdmi_read(cec, HDMI_CEC_RX_DATA1)); |
|---|
| 290 | + input_event(cec->devinput, EV_KEY, KEY_POWER, 1); |
|---|
| 291 | + input_sync(cec->devinput); |
|---|
| 292 | + input_event(cec->devinput, EV_KEY, KEY_POWER, 0); |
|---|
| 293 | + input_sync(cec->devinput); |
|---|
| 294 | + mutex_unlock(&cec->wake_lock); |
|---|
| 295 | + |
|---|
| 296 | + return IRQ_HANDLED; |
|---|
| 297 | +} |
|---|
| 298 | + |
|---|
| 299 | +static int rockchip_hdmi_cec_input_init(struct dw_hdmi_cec *cec) |
|---|
| 300 | +{ |
|---|
| 301 | + int err; |
|---|
| 302 | + |
|---|
| 303 | + cec->devinput = devm_input_allocate_device(cec->dev); |
|---|
| 304 | + if (!cec->devinput) |
|---|
| 305 | + return -EPERM; |
|---|
| 306 | + |
|---|
| 307 | + cec->devinput->name = "hdmi_cec_key"; |
|---|
| 308 | + cec->devinput->phys = "hdmi_cec_key/input0"; |
|---|
| 309 | + cec->devinput->id.bustype = BUS_HOST; |
|---|
| 310 | + cec->devinput->id.vendor = 0x0001; |
|---|
| 311 | + cec->devinput->id.product = 0x0001; |
|---|
| 312 | + cec->devinput->id.version = 0x0100; |
|---|
| 313 | + |
|---|
| 314 | + err = input_register_device(cec->devinput); |
|---|
| 315 | + if (err < 0) { |
|---|
| 316 | + input_free_device(cec->devinput); |
|---|
| 317 | + return err; |
|---|
| 318 | + } |
|---|
| 319 | + input_set_capability(cec->devinput, EV_KEY, KEY_POWER); |
|---|
| 320 | + |
|---|
| 321 | + return 0; |
|---|
| 322 | +} |
|---|
| 323 | + |
|---|
| 324 | +static long cec_standby(struct cec_adapter *adap, __u8 __user *parg) |
|---|
| 325 | +{ |
|---|
| 326 | + u8 en; |
|---|
| 327 | + int ret; |
|---|
| 328 | + struct dw_hdmi_cec *cec = cec_get_drvdata(adap); |
|---|
| 329 | + |
|---|
| 330 | + mutex_lock(&cec->wake_lock); |
|---|
| 331 | + if (copy_from_user(&en, parg, sizeof(en))) { |
|---|
| 332 | + mutex_unlock(&cec->wake_lock); |
|---|
| 333 | + return -EFAULT; |
|---|
| 334 | + } |
|---|
| 335 | + |
|---|
| 336 | + cec->standby_en = !en; |
|---|
| 337 | + ret = adap->ops->adap_enable(adap, en); |
|---|
| 338 | + mutex_unlock(&cec->wake_lock); |
|---|
| 339 | + |
|---|
| 340 | + return ret; |
|---|
| 341 | +} |
|---|
| 342 | + |
|---|
| 343 | +static long cec_func_en(struct dw_hdmi_cec *cec, int __user *parg) |
|---|
| 344 | +{ |
|---|
| 345 | + int en_mask; |
|---|
| 346 | + |
|---|
| 347 | + if (copy_from_user(&en_mask, parg, sizeof(en_mask))) |
|---|
| 348 | + return -EFAULT; |
|---|
| 349 | + |
|---|
| 350 | + cec->wake_en = (en_mask & CEC_EN) && (en_mask & CEC_WAKE); |
|---|
| 351 | + |
|---|
| 352 | + return 0; |
|---|
| 353 | +} |
|---|
| 354 | + |
|---|
| 355 | +static long dw_hdmi_cec_ioctl(struct file *f, unsigned int cmd, unsigned long arg) |
|---|
| 356 | +{ |
|---|
| 357 | + struct dw_hdmi_cec *cec; |
|---|
| 358 | + struct miscdevice *misc_dev; |
|---|
| 359 | + void __user *data; |
|---|
| 360 | + |
|---|
| 361 | + if (!f) |
|---|
| 362 | + return -EFAULT; |
|---|
| 363 | + |
|---|
| 364 | + misc_dev = f->private_data; |
|---|
| 365 | + cec = container_of(misc_dev, struct dw_hdmi_cec, misc_dev); |
|---|
| 366 | + data = (void __user *)arg; |
|---|
| 367 | + |
|---|
| 368 | + switch (cmd) { |
|---|
| 369 | + case CEC_STANDBY: |
|---|
| 370 | + return cec_standby(cec->adap, data); |
|---|
| 371 | + case CEC_FUNC_EN: |
|---|
| 372 | + return cec_func_en(cec, data); |
|---|
| 373 | + default: |
|---|
| 374 | + return -EINVAL; |
|---|
| 375 | + } |
|---|
| 376 | + |
|---|
| 377 | + return -ENOTTY; |
|---|
| 378 | +} |
|---|
| 379 | + |
|---|
| 380 | +static int dw_hdmi_cec_open(struct inode *inode, struct file *f) |
|---|
| 381 | +{ |
|---|
| 382 | + return 0; |
|---|
| 383 | +} |
|---|
| 384 | + |
|---|
| 385 | +static int dw_hdmi_cec_release(struct inode *inode, struct file *f) |
|---|
| 386 | +{ |
|---|
| 387 | + return 0; |
|---|
| 388 | +} |
|---|
| 389 | + |
|---|
| 390 | +static const struct file_operations dw_hdmi_cec_file_operations = { |
|---|
| 391 | + .compat_ioctl = dw_hdmi_cec_ioctl, |
|---|
| 392 | + .unlocked_ioctl = dw_hdmi_cec_ioctl, |
|---|
| 393 | + .open = dw_hdmi_cec_open, |
|---|
| 394 | + .release = dw_hdmi_cec_release, |
|---|
| 395 | + .owner = THIS_MODULE, |
|---|
| 396 | +}; |
|---|
| 397 | + |
|---|
| 398 | +static void dw_hdmi_cec_hpd_wake_up(struct platform_device *pdev) |
|---|
| 399 | +{ |
|---|
| 400 | + struct dw_hdmi_cec *cec = platform_get_drvdata(pdev); |
|---|
| 401 | + |
|---|
| 402 | + mutex_lock(&cec->wake_lock); |
|---|
| 403 | + |
|---|
| 404 | + if (!cec->standby_en) { |
|---|
| 405 | + mutex_unlock(&cec->wake_lock); |
|---|
| 406 | + return; |
|---|
| 407 | + } |
|---|
| 408 | + cec->standby_en = false; |
|---|
| 409 | + |
|---|
| 410 | + dw_hdmi_write(cec, 0x02, HDMI_IH_MUTE); |
|---|
| 411 | + |
|---|
| 412 | + input_event(cec->devinput, EV_KEY, KEY_POWER, 1); |
|---|
| 413 | + input_sync(cec->devinput); |
|---|
| 414 | + input_event(cec->devinput, EV_KEY, KEY_POWER, 0); |
|---|
| 415 | + input_sync(cec->devinput); |
|---|
| 416 | + mutex_unlock(&cec->wake_lock); |
|---|
| 417 | +} |
|---|
| 418 | + |
|---|
| 419 | +static const struct dw_hdmi_cec_wake_ops cec_ops = { |
|---|
| 420 | + .hpd_wake_up = dw_hdmi_cec_hpd_wake_up, |
|---|
| 421 | +}; |
|---|
| 422 | + |
|---|
| 232 | 423 | static int dw_hdmi_cec_probe(struct platform_device *pdev) |
|---|
| 233 | 424 | { |
|---|
| 234 | 425 | struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev); |
|---|
| .. | .. |
|---|
| 247 | 438 | if (!cec) |
|---|
| 248 | 439 | return -ENOMEM; |
|---|
| 249 | 440 | |
|---|
| 441 | + cec->dev = &pdev->dev; |
|---|
| 250 | 442 | cec->irq = data->irq; |
|---|
| 443 | + cec->wake_irq = data->wake_irq; |
|---|
| 251 | 444 | cec->ops = data->ops; |
|---|
| 252 | 445 | cec->hdmi = data->hdmi; |
|---|
| 446 | + |
|---|
| 447 | + mutex_init(&cec->wake_lock); |
|---|
| 253 | 448 | |
|---|
| 254 | 449 | platform_set_drvdata(pdev, cec); |
|---|
| 255 | 450 | |
|---|
| .. | .. |
|---|
| 259 | 454 | dw_hdmi_write(cec, 0, HDMI_CEC_POLARITY); |
|---|
| 260 | 455 | |
|---|
| 261 | 456 | cec->adap = cec_allocate_adapter(&dw_hdmi_cec_ops, cec, "dw_hdmi", |
|---|
| 262 | | - CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | |
|---|
| 263 | | - CEC_CAP_RC | CEC_CAP_PASSTHROUGH, |
|---|
| 457 | + CEC_CAP_DEFAULTS | |
|---|
| 458 | + CEC_CAP_CONNECTOR_INFO, |
|---|
| 264 | 459 | CEC_MAX_LOG_ADDRS); |
|---|
| 265 | 460 | if (IS_ERR(cec->adap)) |
|---|
| 266 | 461 | return PTR_ERR(cec->adap); |
|---|
| 462 | + |
|---|
| 463 | + dw_hdmi_set_cec_adap(cec->hdmi, cec->adap); |
|---|
| 267 | 464 | |
|---|
| 268 | 465 | /* override the module pointer */ |
|---|
| 269 | 466 | cec->adap->owner = THIS_MODULE; |
|---|
| .. | .. |
|---|
| 276 | 473 | |
|---|
| 277 | 474 | ret = devm_request_threaded_irq(&pdev->dev, cec->irq, |
|---|
| 278 | 475 | dw_hdmi_cec_hardirq, |
|---|
| 279 | | - dw_hdmi_cec_thread, IRQF_SHARED, |
|---|
| 476 | + dw_hdmi_cec_thread, IRQF_SHARED | IRQF_ONESHOT, |
|---|
| 280 | 477 | "dw-hdmi-cec", cec->adap); |
|---|
| 281 | 478 | if (ret < 0) |
|---|
| 282 | 479 | return ret; |
|---|
| 283 | 480 | |
|---|
| 284 | | - cec->notify = cec_notifier_get(pdev->dev.parent); |
|---|
| 481 | + if (cec->wake_irq > 0) { |
|---|
| 482 | + ret = devm_request_threaded_irq(&pdev->dev, cec->wake_irq, |
|---|
| 483 | + dw_hdmi_cec_wake_irq, |
|---|
| 484 | + dw_hdmi_cec_wake_thread, |
|---|
| 485 | + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, |
|---|
| 486 | + "cec-wakeup", cec->adap); |
|---|
| 487 | + if (ret) { |
|---|
| 488 | + dev_err(&pdev->dev, |
|---|
| 489 | + "hdmi_cec request_irq failed (%d).\n", |
|---|
| 490 | + ret); |
|---|
| 491 | + return ret; |
|---|
| 492 | + } |
|---|
| 493 | + device_init_wakeup(&pdev->dev, 1); |
|---|
| 494 | + enable_irq_wake(cec->wake_irq); |
|---|
| 495 | + } |
|---|
| 496 | + |
|---|
| 497 | + cec->notify = cec_notifier_cec_adap_register(pdev->dev.parent, |
|---|
| 498 | + NULL, cec->adap); |
|---|
| 285 | 499 | if (!cec->notify) |
|---|
| 286 | 500 | return -ENOMEM; |
|---|
| 287 | 501 | |
|---|
| 288 | 502 | ret = cec_register_adapter(cec->adap, pdev->dev.parent); |
|---|
| 289 | 503 | if (ret < 0) { |
|---|
| 290 | | - cec_notifier_put(cec->notify); |
|---|
| 504 | + cec_notifier_cec_adap_unregister(cec->notify, cec->adap); |
|---|
| 291 | 505 | return ret; |
|---|
| 292 | 506 | } |
|---|
| 293 | 507 | |
|---|
| .. | .. |
|---|
| 297 | 511 | */ |
|---|
| 298 | 512 | devm_remove_action(&pdev->dev, dw_hdmi_cec_del, cec); |
|---|
| 299 | 513 | |
|---|
| 300 | | - cec_register_cec_notifier(cec->adap, cec->notify); |
|---|
| 514 | + rockchip_hdmi_cec_input_init(cec); |
|---|
| 301 | 515 | |
|---|
| 302 | | - return 0; |
|---|
| 516 | + cec->misc_dev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "rk_cec"); |
|---|
| 517 | + if (!cec->misc_dev.name) |
|---|
| 518 | + return -ENOMEM; |
|---|
| 519 | + cec->misc_dev.minor = MISC_DYNAMIC_MINOR; |
|---|
| 520 | + cec->misc_dev.fops = &dw_hdmi_cec_file_operations; |
|---|
| 521 | + cec->misc_dev.mode = 0666; |
|---|
| 522 | + |
|---|
| 523 | + ret = misc_register(&cec->misc_dev); |
|---|
| 524 | + |
|---|
| 525 | + dw_hdmi_cec_wake_ops_register(cec->hdmi, &cec_ops); |
|---|
| 526 | + |
|---|
| 527 | + return ret; |
|---|
| 303 | 528 | } |
|---|
| 304 | 529 | |
|---|
| 305 | 530 | static int dw_hdmi_cec_remove(struct platform_device *pdev) |
|---|
| 306 | 531 | { |
|---|
| 307 | 532 | struct dw_hdmi_cec *cec = platform_get_drvdata(pdev); |
|---|
| 308 | 533 | |
|---|
| 534 | + cec_notifier_cec_adap_unregister(cec->notify, cec->adap); |
|---|
| 309 | 535 | cec_unregister_adapter(cec->adap); |
|---|
| 310 | | - cec_notifier_put(cec->notify); |
|---|
| 536 | + misc_deregister(&cec->misc_dev); |
|---|
| 311 | 537 | |
|---|
| 312 | 538 | return 0; |
|---|
| 313 | 539 | } |
|---|