blob: 32f5a9eaddb6269f7cb695acb4b8cb763acf5c35 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2021 Google LLC. */
#include <libpayload.h>
#include "base/init_funcs.h"
#include "base/list.h"
#include "board/guybrush/include/variant.h"
#include "drivers/bus/i2c/cros_ec_tunnel.h"
#include "drivers/bus/i2c/designware.h"
#include "drivers/bus/i2c/i2c.h"
#include "drivers/ec/cros/lpc.h"
#include "drivers/ec/anx3429/anx3429.h"
#include "drivers/ec/ps8751/ps8751.h"
#include "drivers/gpio/gpio.h"
#include "drivers/sound/gpio_amp.h"
#include "drivers/gpio/kern.h"
#include "drivers/gpio/sysinfo.h"
#include "drivers/power/fch.h"
#include "drivers/soc/cezanne.h"
#include "drivers/sound/amd_acp.h"
#include "drivers/sound/rt1019.h"
#include "drivers/sound/rt5682s.h"
#include "drivers/storage/ahci.h"
#include "drivers/storage/blockdev.h"
#include "drivers/tpm/cr50_i2c.h"
#include "drivers/tpm/tpm.h"
#include "drivers/video/display.h"
#include "drivers/bus/usb/usb.h"
#include "pci.h"
#include "vboot/util/flag.h"
/* Clock frequencies */
#define MCLK 48000000
#define LRCLK 8000
/* eDP backlight */
#define GPIO_BACKLIGHT 129
/* Audio Configuration */
#define AUDIO_I2C_MMIO_ADDR 0xfedc4000
#define AUDIO_I2C_SPEED 400000
#define I2C_DESIGNWARE_CLK_MHZ 150
static unsigned int cr50_int_gpio = CR50_INT_85;
__weak const struct storage_config *variant_get_storage_configs(size_t *count)
{
static const struct storage_config storage_configs[] = {
{ .media = STORAGE_NVME, .pci_dev = PCI_DEV(0, 0x02, 0x04)},
{ .media = STORAGE_SDHCI, .pci_dev = PCI_DEV(0, 0x02, 0x02)},
};
*count = ARRAY_SIZE(storage_configs);
return storage_configs;
}
__weak unsigned int variant_get_cr50_irq_gpio(void)
{
return CR50_INT_85;
}
__weak SoundRouteComponent *variant_get_audio_codec(I2cOps *i2c, uint8_t chip,
uint32_t mclk,
uint32_t lrclk)
{
rt5682sCodec *rt5682s = new_rt5682s_codec(i2c, chip, mclk, lrclk);
return &rt5682s->component;
}
__weak enum audio_amp variant_get_audio_amp(void)
{
return AUDIO_AMP_RT1019;
}
__weak unsigned int variant_get_en_spkr_gpio(void)
{
return EN_SPKR;
}
static int cr50_irq_status(void)
{
static KernGpio *tpm_gpio;
if (!tpm_gpio)
tpm_gpio = new_kern_fch_gpio_latched(cr50_int_gpio);
return gpio_get(&tpm_gpio->ops);
}
static int guybrush_backlight_update(DisplayOps *me, uint8_t enable)
{
/* Backlight signal is active low */
static KernGpio *backlight;
if (!backlight)
backlight = new_kern_fch_gpio_output(GPIO_BACKLIGHT, !enable);
else
backlight->ops.set(&backlight->ops, !enable);
return 0;
}
static DisplayOps guybrush_display_ops = {
.backlight_update = &guybrush_backlight_update,
};
static void setup_amd_acp_i2s(pcidev_t acp_pci_dev)
{
AmdAcp *acp = new_amd_acp(acp_pci_dev, ACP_OUTPUT_PLAYBACK, LRCLK,
0x1FFF /* Volume */);
SoundRoute *sound_route = new_sound_route(&acp->sound_ops);
DesignwareI2c *i2c = new_designware_i2c((uintptr_t)AUDIO_I2C_MMIO_ADDR,
AUDIO_I2C_SPEED, I2C_DESIGNWARE_CLK_MHZ);
SoundRouteComponent *codec =
variant_get_audio_codec(&i2c->ops, 0x1a, MCLK, LRCLK);
KernGpio *en_spkr_gpio =
new_kern_fch_gpio_output(variant_get_en_spkr_gpio(), 0);
/* Note: component order matters here. The first thing inserted is the
* last thing enabled. */
/* If a warm reboot was performed, we need to clear out any settings the
* OS has made to the RT1019. */
if (variant_get_audio_amp() == AUDIO_AMP_RT1019) {
rt1019Codec *speaker_amp =
new_rt1019_codec(&i2c->ops, AUD_RT1019_DEVICE_ADDR_R);
list_insert_after(&speaker_amp->component.list_node,
&sound_route->components);
GpioAmpCodec *en_spkr = new_gpio_amp_codec(&en_spkr_gpio->ops);
/* We need to enable the speaker amp before we can send I2C
* transactions. */
list_insert_after(&en_spkr->component.list_node,
&sound_route->components);
} else {
/* Give the MAX98360A time to sample the BCLK */
GpioAmpCodec *en_spkr =
new_gpio_amp_codec_with_delay(&en_spkr_gpio->ops, 1000);
/* Enable the speaker amp last since we have to delay to sample
* the clocks. */
list_insert_after(&en_spkr->component.list_node,
&sound_route->components);
}
/* Enable the ACP so it can start sampling the clocks. */
list_insert_after(&acp->component.list_node, &sound_route->components);
/*
* We want the codec to initialize first so it can start generating the
* BCLK/LRCLK.
*/
list_insert_after(&codec->list_node, &sound_route->components);
sound_set_ops(&sound_route->ops);
}
static int board_setup(void)
{
sysinfo_install_flags(NULL);
cr50_int_gpio = variant_get_cr50_irq_gpio();
CrosEcLpcBus *cros_ec_lpc_bus =
new_cros_ec_lpc_bus(CROS_EC_LPC_BUS_GENERIC);
CrosEc *cros_ec = new_cros_ec(&cros_ec_lpc_bus->ops, NULL);
register_vboot_ec(&cros_ec->vboot);
flag_replace(FLAG_LIDSW, cros_ec_lid_switch_flag());
flag_replace(FLAG_PWRSW, cros_ec_power_btn_flag());
pcidev_t acp_pci_dev;
if (pci_find_device(AMD_PCI_VID, AMD_FAM17H_ACP_PCI_DID, &acp_pci_dev))
setup_amd_acp_i2s(acp_pci_dev);
/* Set up H1 / Dauntless on I2C3 */
DesignwareI2c *i2c_h1 = new_designware_i2c(
AP_I2C3_ADDR, 400000, AP_I2C_CLK_MHZ);
tpm_set_ops(&new_cr50_i2c(&i2c_h1->ops, 0x50,
&cr50_irq_status)->base.ops);
power_set_ops(&kern_power_ops);
display_set_ops(&guybrush_display_ops);
return 0;
}
INIT_FUNC(board_setup);