| /* |
| * hda_i915.c - routines for Haswell HDA controller power well support |
| * |
| * 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, write to the Free Software Foundation, |
| * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/module.h> |
| #include <linux/pci.h> |
| #include <linux/component.h> |
| #include <drm/i915_component.h> |
| #include <sound/core.h> |
| #include "hda_priv.h" |
| #include "hda_intel.h" |
| |
| /* Intel HSW/BDW display HDA controller Extended Mode registers. |
| * EM4 (M value) and EM5 (N Value) are used to convert CDClk (Core Display |
| * Clock) to 24MHz BCLK: BCLK = CDCLK * M / N |
| * The values will be lost when the display power well is disabled. |
| */ |
| #define AZX_REG_EM4 0x100c |
| #define AZX_REG_EM5 0x1010 |
| |
| int hda_display_power(struct hda_intel *hda, bool enable) |
| { |
| struct i915_audio_component *acomp = &hda->audio_component; |
| |
| if (!acomp->ops) |
| return -ENODEV; |
| |
| dev_dbg(&hda->chip.pci->dev, "display power %s\n", |
| enable ? "enable" : "disable"); |
| if (enable) |
| acomp->ops->get_power(acomp->dev); |
| else |
| acomp->ops->put_power(acomp->dev); |
| |
| return 0; |
| } |
| |
| void haswell_set_bclk(struct hda_intel *hda) |
| { |
| int cdclk_freq; |
| unsigned int bclk_m, bclk_n; |
| struct i915_audio_component *acomp = &hda->audio_component; |
| |
| if (!acomp->ops) |
| return; |
| |
| cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev); |
| switch (cdclk_freq) { |
| case 337500: |
| bclk_m = 16; |
| bclk_n = 225; |
| break; |
| |
| case 450000: |
| default: /* default CDCLK 450MHz */ |
| bclk_m = 4; |
| bclk_n = 75; |
| break; |
| |
| case 540000: |
| bclk_m = 4; |
| bclk_n = 90; |
| break; |
| |
| case 675000: |
| bclk_m = 8; |
| bclk_n = 225; |
| break; |
| } |
| |
| azx_writew(&hda->chip, EM4, bclk_m); |
| azx_writew(&hda->chip, EM5, bclk_n); |
| } |
| |
| static int hda_component_master_bind(struct device *dev) |
| { |
| struct snd_card *card = dev_get_drvdata(dev); |
| struct azx *chip = card->private_data; |
| struct hda_intel *hda = container_of(chip, struct hda_intel, chip); |
| struct i915_audio_component *acomp = &hda->audio_component; |
| int ret; |
| |
| ret = component_bind_all(dev, acomp); |
| if (ret < 0) |
| return ret; |
| |
| if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power && |
| acomp->ops->put_power && acomp->ops->get_cdclk_freq))) { |
| ret = -EINVAL; |
| goto out_unbind; |
| } |
| |
| /* |
| * Atm, we don't support dynamic unbinding initiated by the child |
| * component, so pin its containing module until we unbind. |
| */ |
| if (!try_module_get(acomp->ops->owner)) { |
| ret = -ENODEV; |
| goto out_unbind; |
| } |
| |
| return 0; |
| |
| out_unbind: |
| component_unbind_all(dev, acomp); |
| |
| return ret; |
| } |
| |
| static void hda_component_master_unbind(struct device *dev) |
| { |
| struct snd_card *card = dev_get_drvdata(dev); |
| struct azx *chip = card->private_data; |
| struct hda_intel *hda = container_of(chip, struct hda_intel, chip); |
| struct i915_audio_component *acomp = &hda->audio_component; |
| |
| module_put(acomp->ops->owner); |
| component_unbind_all(dev, acomp); |
| WARN_ON(acomp->ops || acomp->dev); |
| } |
| |
| static const struct component_master_ops hda_component_master_ops = { |
| .bind = hda_component_master_bind, |
| .unbind = hda_component_master_unbind, |
| }; |
| |
| static int hda_component_master_match(struct device *dev, void *data) |
| { |
| /* i915 is the only supported component */ |
| return !strcmp(dev->driver->name, "i915"); |
| } |
| |
| int hda_i915_init(struct hda_intel *hda) |
| { |
| struct component_match *match = NULL; |
| struct device *dev = &hda->chip.pci->dev; |
| struct i915_audio_component *acomp = &hda->audio_component; |
| int ret; |
| |
| component_match_add(dev, &match, hda_component_master_match, hda); |
| ret = component_master_add_with_match(dev, &hda_component_master_ops, |
| match); |
| if (ret < 0) |
| goto out_err; |
| |
| /* |
| * Atm, we don't support deferring the component binding, so make sure |
| * i915 is loaded and that the binding successfully completes. |
| */ |
| request_module("i915"); |
| |
| if (!acomp->ops) { |
| ret = -ENODEV; |
| goto out_master_del; |
| } |
| |
| dev_dbg(dev, "bound to i915 component master\n"); |
| |
| return 0; |
| out_master_del: |
| component_master_del(dev, &hda_component_master_ops); |
| out_err: |
| dev_err(dev, "failed to add i915 component master (%d)\n", ret); |
| |
| return ret; |
| } |
| |
| int hda_i915_exit(struct hda_intel *hda) |
| { |
| struct device *dev = &hda->chip.pci->dev; |
| |
| component_master_del(dev, &hda_component_master_ops); |
| |
| return 0; |
| } |